Go 102: A Workshop

2a32a8864a24ef5c1ce26260d5eebdd6?s=47 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

2a32a8864a24ef5c1ce26260d5eebdd6?s=128

timblair

November 26, 2015
Tweet

Transcript

  1. 5.

    The Language Designed for concurrent systems programming, Strongly and statically

    typed, Compiled, Garbage collected, Object oriented (ish), Small, opinionated, and done.
  2. 6.
  3. 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"
  4. 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
  5. 11.

    Zero Values 0 // numeric false // boolean "" //

    string nil // pointer, channel, func, // interface, map, or slice
  6. 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
  7. 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" }
  8. 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.
  9. 15.

    What have we covered? Built-in Types & value declaration All

    types have a zero value structs as collections of named fields functions
  10. 19.

    Object Orientation OOP is about objects. An object is a

    data structure that has both state and behaviour
  11. 20.
  12. 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 }
  13. 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 }
  14. 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 }
  15. 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.
  16. 27.

    Methods methods are functions that are bound to a receiver

    A receiver can be any named type Methods are effectively syntactic sugar
  17. 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() { /* ... */ }
  18. 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
  19. 34.

    Interfaces type email struct { name string address string }

    e := email{"Tim Blair", "tim@bla.ir"} fmt.Println(e) // {Tim Blair tim@bla.ir}
  20. 35.
  21. 36.

    Interfaces type email struct { name string address string }

    func (e email) String() string { return fmt.Sprintf("\"%s\" <%s>", e.name, e.address) } e := email{"Tim Blair", "tim@bla.ir"} fmt.Println(e) // "Tim Blair" <tim@bla.ir>
  22. 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.
  23. 38.

    Interfaces Interfaces are types that declare behaviour they provide polymorphism

    behaviour no `implements` keyword; satisfied implicitly
  24. 39.
  25. 40.

    Embedding type sphere struct { x, y, z, radius int

    } type cube struct { x, y, z, length int }
  26. 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
  27. 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
  28. 43.

    Embedding s1 := sphere{point{1, 2, 3}, 5} s2 := sphere{

    point: point{ 1, 2, 3, }, radius: 5, // required trailing comma }
  29. 44.

    Embedding type robot struct { } func (r robot) talk()

    { fmt.Println("Bzzzzzbt") } type robby struct { robot } robby := robby{} robby.talk() // Bzzzzzbt
  30. 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?
  31. 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{})
  32. 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.
  33. 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
  34. 50.

    Composition "Everyone knows composition is more powerful than inheritance, Go

    just makes this non optional." –– Dave Cheney: http://bit.ly/dctlg
  35. 51.

    Composition type point struct { x, y int } type

    mover interface { moveTo(p point) } type firer interface { fire() }
  36. 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 }
  37. 54.

    Composition type moverFirer interface { mover firer } func moveAndFire(mf

    moverFirer, p point) { mf.moveTo(p) mf.fire() }
  38. 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 }
  39. 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
  40. 62.

    goroutines func doWork(i int) { time.Sleep(time.Millisecond * 500) fmt.Println(i) }

    func main() { for i := 1; i <= 5; i++ { doWork(i) } }
  41. 63.

    goroutines $ time go run g1.go 1 2 3 4

    5 go run g1.go ... 2.757 total
  42. 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! } }
  43. 67.

    Goroutines import "sync" func main() { var wg sync.WaitGroup wg.Add(1)

    go func() { // Do work... wg.Done() }() wg.Wait() }
  44. 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() }
  45. 69.

    goroutines $ time go run g3.go 4 1 5 2

    3 go run g3.go ... 0.752 total
  46. 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.
  47. 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
  48. 72.
  49. 73.

    Channels "Do not communicate by sharing memory; instead, share memory

    by communicating." -- https://golang.org/doc/effective_go.html
  50. 74.

    Channels ch := make(chan int) ch <- 42 // Send

    a value to the channel v := <-ch // Receive a value from ch
  51. 76.

    Channels c1 := make(chan int) // Unbuffered c2 := make(chan

    int, 0) // Unbuffered c3 := make(chan int, 100) // Buffered
  52. 80.
  53. 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 <- search(q, x) }(s) } return <-res } servers := []string{"s1", "s2", "s3"} fmt.Println(parallelSearch("foo", servers))
  54. 82.

    Channels ch := make(chan int) close(ch) // Close the channel

    for writing v, ok := <-ch // Receive, testing for closure if !ok { fmt.Println("Channel closed!") }
  55. 83.

    Channels ch := make(chan int) // Read from channel until

    it is closed for i := range ch { fmt.Println(i) }
  56. 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 <- struct{}{} // Serve the "ball." wg.Wait() // Wait for the game to finish. }
  57. 85.

    Channels func player(name string, court chan struct{}) { for {

    ball := <-court fmt.Println(name, "hit the ball") court <- ball // Hit the ball back. } }
  58. 86.

    Channels func player(name string, court chan struct{}) { for {

    ball, ok := <-court if !ok { // If the channel was closed we won. fmt.Println(name, "won!") return } fmt.Println(name, "hit the ball") court <- ball // Hit the ball back. } }
  59. 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 <- ball // Hit the ball back. } }
  60. 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!
  61. 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.
  62. 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