Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go 102: A Workshop

timblair
November 26, 2015

Go 102: A Workshop

This is the slide deck to accompany the workshop I gave on 2015-11-26. The content covers:

* A quick introduction: built-in types, variable declaration, function and custom types.
* Object oriented development: methods, interfaces, embedding and composition.
* Concurrency: goroutines and channels.

Each section ended with a hands-on exercise to apply the information from that section. The detailed material from the workshop (including the exercises) is available at: https://github.com/timblair/go-102

timblair

November 26, 2015
Tweet

More Decks by timblair

Other Decks in Programming

Transcript

  1. Go 102
    A Go Workshop

    View Slide

  2. Topics
    Go Basics
    Object Oriented Programming in Go
    Concurrency

    View Slide

  3. Information
    / timblair / go-102-workshop

    View Slide

  4. The Basics

    View Slide

  5. The Language
    Designed for concurrent systems programming,
    Strongly and statically typed, Compiled, Garbage
    collected, Object oriented (ish), Small, opinionated,
    and done.

    View Slide

  6. Hello World
    package main
    import (
    "fmt"
    )
    func main() {
    fmt.Println("Hello, World!")
    }

    View Slide

  7. Types
    Arrays
    slices
    maps
    booleans
    numerics
    strings
    pointers
    structs
    channels

    View Slide

  8. Types
    Arrays
    slices
    maps
    booleans
    numerics
    strings
    pointers
    structs
    channels

    View Slide

  9. Basic Types
    b1 := true // type is bool
    b2 := false
    n1 := 123 // int
    n2 := 123.456 // float32/64
    n3 := 1e10 // float32/64
    n4 := uint8(123) // uint
    n5 := float32(123) // float32
    s1 := `Raw string literal`
    s2 := "Interpreted string literal"

    View Slide

  10. Variable Declaration
    var x T // Variable x of type T with a zero value
    var x T = v // Variable x of type T with value v
    var x = v // Variable x with value v, implicit typing
    x := v // Short variable declaration (type inferred)
    x, y := v1, v2 // Double declaration (similar with var)
    make(T) // make takes a type T, which must be a slice,
    // map or channel type, optionally followed by
    // a type-specific list of expressions

    View Slide

  11. Zero Values
    0 // numeric
    false // boolean
    "" // string
    nil // pointer, channel, func,
    // interface, map, or slice

    View Slide

  12. Structs
    type rectangle struct {
    width int
    height int
    }
    r1 := rectangle{1, 2} // New rectangle with w + h
    r1.width = 3 // Set width to a new value
    fmt.Printf("Width = %d; Height = %d\n", r1.width, r1.height)
    var r2 rectangle // w=0, h=0 (int zero values)
    r4 := rectangle{} // w=0, h=0
    r3 := rectangle{height: 1} // w=0, h=1

    View Slide

  13. Functions
    func f1() {} // Simple function definition
    func f2(s string, i int) {} // Function that accepts two args
    func f3(s1, s2 string) {} // Two args of the same type
    func f4(s ...string) {} // Variadic function
    func f5() int { // Return type declaration
    return 42
    }
    func f6() (int, string) { // Multiple return values
    return 42, "foo"
    }

    View Slide

  14. Exercise
    Declare a struct type to maintain information about
    a person. Declare a function that creates new
    values of your type. Call this function from main
    and display the value.

    View Slide

  15. What have we covered?
    Built-in Types & value declaration
    All types have a zero value
    structs as collections of named fields
    functions

    View Slide

  16. Object Orientation

    View Slide

  17. Object Orientation
    Is Go an object-oriented language?
    Yes and no.

    View Slide

  18. Object Orientation
    Abstraction
    Encapsulation
    Inheritance
    Polymorphism

    View Slide

  19. Object Orientation
    OOP is about objects.
    An object is a data structure that
    has both state and behaviour

    View Slide

  20. Methods

    View Slide

  21. Methods
    type rectangle struct {
    width int
    height int
    }
    func area(r rectangle) int {
    return r.width * r.height
    }
    func main() {
    r := rectangle{3, 4}
    fmt.Println(area(r)) // area 12
    }

    View Slide

  22. Methods
    A method is a function bound to a receiver

    View Slide

  23. Methods
    type rectangle struct {
    width int
    height int
    }
    func (r rectangle) area() int {
    return r.width * r.height
    }
    func main() {
    r := rectangle{3, 4}
    fmt.Println(r.area()) // area 12
    }

    View Slide

  24. Methods
    A receiver can be any named type

    View Slide

  25. Methods
    // function, called with area(r)
    func area(r rectangle) int {
    return r.width * r.height
    }
    // method, called with r.area()
    func (r rectangle) area() int {
    return r.width * r.height
    }

    View Slide

  26. Methods
    Declare a new struct type to hold information about a
    tennis player, including the number of matches played
    and the number won. Add a method to this type that
    calculates the win ratio for the player. Create a new
    player, and output the win ratio for them.

    View Slide

  27. Methods
    methods are functions that are bound to a receiver
    A receiver can be any named type
    Methods are effectively syntactic sugar

    View Slide

  28. Interfaces

    View Slide

  29. Interfaces
    Provide polymorphism
    Declare behaviour

    View Slide

  30. Interfaces
    no implements keyword
    interfaces are satisfied implicitly

    View Slide

  31. Interfaces
    type speaker interface {
    speak() string
    }

    View Slide

  32. Interfaces
    type hipster struct { }
    func (h hipster) speak() string { return "Amazeballs" }
    func (h hipster) trimBeard() { /* ... */ }
    type dog struct { }
    func (d dog) speak() string { return "Woof" }
    func (d dog) wagTail() { /* ... */ }
    type robot struct { }
    func (r robot) speak() string { return "Does not compute" }
    func (r robot) becomeSentient() { /* ... */ }

    View Slide

  33. Interfaces
    // We can treat a hipster as a speaker
    var s1 speaker
    s1 = hipster{}
    // We can also create a slice of different speakers
    speakers := []speaker{hipster{}, dog{}, robot{}}
    for _, s := range speakers {
    fmt.Printf("%T: %s\n", s, s.speak())
    }
    // main.hipster: Amazeballs
    // main.dog: Woof
    // main.robot: Does not compute

    View Slide

  34. Interfaces
    type email struct {
    name string
    address string
    }
    e := email{"Tim Blair", "[email protected]"}
    fmt.Println(e)
    // {Tim Blair [email protected]}

    View Slide

  35. Interfaces
    // The Stringer interface found in fmt package
    type Stringer interface {
    String() string
    }

    View Slide

  36. Interfaces
    type email struct {
    name string
    address string
    }
    func (e email) String() string {
    return fmt.Sprintf("\"%s\" ", e.name, e.address)
    }
    e := email{"Tim Blair", "[email protected]"}
    fmt.Println(e)
    // "Tim Blair"

    View Slide

  37. Interfaces
    Define an interface with a method Area(). Create types
    for Square, Rectangle and Circle, and ensure they
    satisfy your interface. Create a function that accepts
    a value of your interface type and outputs the area,
    and call this function for different shapes.

    View Slide

  38. Interfaces
    Interfaces are types that declare behaviour
    they provide polymorphism behaviour
    no `implements` keyword; satisfied implicitly

    View Slide

  39. Embedding

    View Slide

  40. Embedding
    type sphere struct {
    x, y, z, radius int
    }
    type cube struct {
    x, y, z, length int
    }

    View Slide

  41. Embedding
    type point struct {
    x, y, z int
    }
    type sphere struct {
    point point
    radius int
    }
    type cube struct {
    point point
    length int
    }
    var s sphere
    s.point.x = 5
    s.point.y = 6
    s.point.z = 7
    s.radius = 3

    View Slide

  42. Embedding
    type point struct {
    x, y, z int
    }
    type sphere struct {
    point
    radius int
    }
    type cube struct {
    point
    length int
    }
    var s sphere
    s.x = 5
    s.y = 6
    s.z = 7
    s.radius = 3

    View Slide

  43. Embedding
    s1 := sphere{point{1, 2, 3}, 5}
    s2 := sphere{
    point: point{
    1,
    2,
    3,
    },
    radius: 5, // required trailing comma
    }

    View Slide

  44. Embedding
    type robot struct { }
    func (r robot) talk() { fmt.Println("Bzzzzzbt") }
    type robby struct {
    robot
    }
    robby := robby{}
    robby.talk() // Bzzzzzbt

    View Slide

  45. Embedding
    type robot struct { }
    func (r robot) talk() { fmt.Println("Bzzzzzbt") }
    type robby struct {
    robot
    }
    func (r robby) talk() { fmt.Println("Again?") }
    robby := robby{}
    robby.talk() // Again?

    View Slide

  46. Embedding
    type talker interface { talk() }
    type robot struct{}
    func (r robot) talk() { fmt.Println("Bzzzzzbt") }
    type robby struct { robot }
    func talk(t talker) {
    t.talk()
    }
    talk(robby{})

    View Slide

  47. Embedding
    Create a user type, and an admin type that embeds a
    user. Create a Notifier interface, and make your user type
    satisfy that interface. Write a function that accepts a
    value of the interface type, and ensure it works
    correctly when passed a value of your admin type.

    View Slide

  48. Embedding
    composition is supported through type embedding
    anonymous struct fields are said to be embedded
    Both inner types and methods are promoted
    Promoted methods can satisfy an interface

    View Slide

  49. Composition

    View Slide

  50. Composition
    "Everyone knows composition is more powerful than
    inheritance, Go just makes this non optional."
    –– Dave Cheney: http://bit.ly/dctlg

    View Slide

  51. Composition
    type point struct {
    x, y int
    }
    type mover interface {
    moveTo(p point)
    }
    type firer interface {
    fire()
    }

    View Slide

  52. Composition
    type vehicle struct {
    point
    passengers int
    }
    func (v *vehicle) moveTo(p point) { v.point = p }
    type weapon struct {
    loaded bool
    }
    func (w *weapon) fire() { w.loaded = false }

    View Slide

  53. Composition
    type tank struct {
    vehicle
    weapon
    }

    View Slide

  54. Composition
    type moverFirer interface {
    mover
    firer
    }
    func moveAndFire(mf moverFirer, p point) {
    mf.moveTo(p)
    mf.fire()
    }

    View Slide

  55. Composition
    func main() {
    t := &tank{
    vehicle{point{5, 6}, 6},
    weapon{true},
    }
    moveAndFire(t, point{10, 20})
    fmt.Printf("Location: %v; Passengers: %d; Loaded: %t\n",
    t.point, t.passengers, t.loaded)
    // Location: {10 20}; Passengers: 6; Loaded: false
    }

    View Slide

  56. Composition
    Composition is a design pattern
    discrete behaviours are defined through interfaces
    they are implemented via methods in concrete types
    Simple behaviours are composed to give complexity

    View Slide

  57. Concurrency

    View Slide

  58. Concurrency
    concurrency is not parallelism

    View Slide

  59. Concurrency
    Go has concurrency primitives built-in:
    goroutines for concurrent functions
    channels for communicating between goroutines

    View Slide

  60. Goroutines

    View Slide

  61. Goroutines
    Lightweight threads of execution
    multiplexed across os threads
    very cheap to use

    View Slide

  62. goroutines
    func doWork(i int) {
    time.Sleep(time.Millisecond * 500)
    fmt.Println(i)
    }
    func main() {
    for i := 1; i <= 5; i++ {
    doWork(i)
    }
    }

    View Slide

  63. goroutines
    $ time go run g1.go
    1
    2
    3
    4
    5
    go run g1.go ... 2.757 total

    View Slide

  64. goroutines
    go someFunc() // Concurrency!

    View Slide

  65. goroutines
    func doWork(i int) {
    time.Sleep(time.Millisecond * 500)
    fmt.Println(i)
    }
    func main() {
    for i := 1; i <= 5; i++ {
    go doWork(i) // Concurrency!
    }
    }

    View Slide

  66. goroutines
    $ time go run g2.go
    go run g2.go ... 0.247 total

    View Slide

  67. Goroutines
    import "sync"
    func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
    // Do work...
    wg.Done()
    }()
    wg.Wait()
    }

    View Slide

  68. GOroutines
    func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 5; i++ {
    wg.Add(1)
    go func(x int) {
    defer wg.Done()
    doWork(x)
    }(i)
    }
    wg.Wait()
    }

    View Slide

  69. goroutines
    $ time go run g3.go
    4
    1
    5
    2
    3
    go run g3.go ... 0.752 total

    View Slide

  70. Goroutines
    Create two anonymous functions: one that outputs
    integers from 1 to 100; the other from 100 to 1. Start
    each function as a goroutine. Use a WaitGroup to ensure
    that main() doesn't exit until the goroutines are done.

    View Slide

  71. Goroutines
    A Goroutine is a function running independently
    Effectively Lightweight threads
    Multiplexed against one or more OS threads
    WaitGroups can be used to signal goroutine completion

    View Slide

  72. Channels

    View Slide

  73. Channels
    "Do not communicate by sharing memory;
    instead, share memory by communicating."
    -- https://golang.org/doc/effective_go.html

    View Slide

  74. Channels
    ch := make(chan int)
    ch v :=

    View Slide

  75. Channels
    Buffered vs. Unbuffered

    View Slide

  76. Channels
    c1 := make(chan int) // Unbuffered
    c2 := make(chan int, 0) // Unbuffered
    c3 := make(chan int, 100) // Buffered

    View Slide

  77. Unbuffered Channels
    Image from github.com/ardenlabs/gotraining

    View Slide

  78. Buffered Channels
    Image from github.com/ardenlabs/gotraining

    View Slide

  79. Channels
    c2 := make(chan int, 1) // A buffered channel
    c2 v :=

    View Slide

  80. Channels
    func doWork(done chan bool) {
    fmt.Println("Working...")
    time.Sleep(time.Second)
    fmt.Println("Done.")
    done }
    func main() {
    done := make(chan bool)
    go doWork(done)
    }

    View Slide

  81. Channels
    func search(q string, server string) string { /* ... */ }
    func parallelSearch(q string, servers []string) string {
    res := make(chan string, 3)
    for _, s := range servers {
    go func(x string) { res }
    return }
    servers := []string{"s1", "s2", "s3"}
    fmt.Println(parallelSearch("foo", servers))

    View Slide

  82. Channels
    ch := make(chan int)
    close(ch) // Close the channel for writing
    v, ok := if !ok {
    fmt.Println("Channel closed!")
    }

    View Slide

  83. Channels
    ch := make(chan int)
    // Read from channel until it is closed
    for i := range ch {
    fmt.Println(i)
    }

    View Slide

  84. Channels
    var wg sync.WaitGroup
    func main() {
    court := make(chan struct{}) // An unbuffered channel.
    wg.Add(2) // Add two to the WG, one for each player.
    // Launch two players.
    go player("Serena", court)
    go player("Venus", court)
    court wg.Wait() // Wait for the game to finish.
    }

    View Slide

  85. Channels
    func player(name string, court chan struct{}) {
    for {
    ball := fmt.Println(name, "hit the ball")
    court }
    }

    View Slide

  86. Channels
    func player(name string, court chan struct{}) {
    for {
    ball, ok := if !ok { // If the channel was closed we won.
    fmt.Println(name, "won!")
    return
    }
    fmt.Println(name, "hit the ball")
    court }
    }

    View Slide

  87. Channels
    func player(name string, court chan struct{}) {
    for {
    // Receive step excluded...
    if rand.Intn(10) == 0 { // Decide if we missed the ball.
    fmt.Println(name, "missed the ball")
    close(court) // Close the channel to signal we lost.
    return
    }
    fmt.Println(name, "hit the ball")
    court }
    }

    View Slide

  88. Channels
    func player(name string, court chan struct{}) {
    defer wg.Done()
    for {
    // ...
    }
    }

    View Slide

  89. Channels
    $ go run tennis.go
    Venus hit the ball
    Serena hit the ball
    Venus hit the ball
    Serena hit the ball
    Venus hit the ball
    Serena hit the ball
    Venus missed the ball
    Serena won!

    View Slide

  90. Channels
    Create a channel representing a track, and a function
    representing a runner. Pass a baton between runners
    over the channel, and end the race when the fourth
    runner receives the baton.

    View Slide

  91. Goroutines
    Channels are used to communicate between goroutines
    Avoids the need for locking around data structures
    Channels can be buffered or unbuffered
    Closing a channel can be used as a signalling mechanism

    View Slide

  92. and finally...

    View Slide

  93. What have we covered?
    OOP: interfaces, methods and embedding
    Concurrency: goroutines and channels

    View Slide

  94. Next Steps
    https://tour.golang.org/
    https://golang.org/doc/effective_go.html
    https://github.com/ardanlabs/gotraining
    https://github.com/golang/go/wiki

    View Slide

  95. Next Steps

    View Slide

  96. Feedback
    http:// bit.ly / g102-feedback

    View Slide

  97. The end
    @timblair

    View Slide