Top Banner
How To Think In Go or, let me tell you about all the ways I screwed up
62

How To Think In Go

Jan 11, 2017

Download

Internet

lestrrat
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: How To Think In Go

How To Think In Goor, let me tell you about all the ways I screwed up

Page 2: How To Think In Go
Page 3: How To Think In Go
Page 4: How To Think In Go
Page 5: How To Think In Go
Page 6: How To Think In Go

https://www.flickr.com/photos/spam/3355824586

Page 7: How To Think In Go

Panics and Errors

Page 8: How To Think In Go

“Oh look, panic() and rescue()! I can probably use it like lightweight exceptions!”

Misconception

Page 9: How To Think In Go
Page 10: How To Think In Go

! panic: unrecoverable errors ! error: recoverable errors

panic / error

Page 11: How To Think In Go

sub foo { eval { might_die(); }; if ($@) { # handle $@ } important_task(); }

Perl: Exceptions

Page 12: How To Think In Go

func main() { defer func() { if err := recover(); err != nil { // handle err } }() mightPanic() importantTask() // Never reached if mightPanic() panics }

A bad port

Page 13: How To Think In Go

func mightPanic() { defer func() { if err := recover(); err != nil { // handle err } }() panic(“BOO!”) }

func main() { mightPanic() importantTask() }

Attempt #2

Page 14: How To Think In Go

func main() { if err := tryit(mightPanic); err != nil { fmt.Printf(“Found error: %s\n”, err) } importantTask() }

func mightPanic() { panic(“BOO!”) }

Attempt #3

Page 15: How To Think In Go

func tryit(code func()) (err error) {     defer func() {         if e := recover(); e != nil {             var ok bool             if err, ok = e.(error); !ok {                 err = fmt.Errorf("%s", e)             }         }     }()     code()     return err }

Attempt #3

Page 16: How To Think In Go
Page 17: How To Think In Go

“Why don’t I just use plain errors to begin with?”

Go Way

Page 18: How To Think In Go

func mightError() error {      if errorCase {           return errors.New(“error case 1”)      }      if err := someOtherThingMightError(); err != nil {           // propagate error           return err      }      return nil }

Use errors!

Page 19: How To Think In Go

func main() {      if err := mightError(); err != nil {          // handle it      }      importantTask() }

Use errors!

Page 20: How To Think In Go

! Do not dream about exceptions ! Do stick with errors

Use errors!

Page 21: How To Think In Go

Concurrency

Page 22: How To Think In Go

“Go’s makes concurrency so easy, we can just port our multi-process code in an instant!”

Misconception

Page 23: How To Think In Go
Page 24: How To Think In Go

! PIDs to identify processes ! Processes can be signaled ! Processes can notify termination

Processes

Page 25: How To Think In Go

! No pid to identify goroutines ! No signals to communicate ! Goroutines don’t notify on exit

Goroutine

Page 26: How To Think In Go

sub sub main {      my $pid = fork();      if (! defined $pid) { die “Failed to fork: $!” }      if (! $pid) { while(1) { do_stuff() } }      $SIG{CHLD} = sub {            my $pid = wait;            print “reaped $pid\n”;      };

     sleep 5;      kill TERM => $pid;      while (kill 0 => $pid) { sleep 1 } }

Perl: Processes

Page 27: How To Think In Go

func main() { // Won’t work     go func() { for { doStuff() } } }

Go: A simple goroutine

Page 28: How To Think In Go

func main() {     exitCh := make(chan struct{}) go func() { defer func() { close(exitCh) }() // close upon termination for { doStuff() } } <-exitCh // Wait for something to happen }

Go: Detect termination

Page 29: How To Think In Go

func main() {     exitCh := make(chan struct{}) incomingCh := make(chan struct{}) go func() { defer func() { close(exitCh) }() // close upon termination for { select { case <-incomingCh: // Got termination request return } doStuff() } }

Go: Accept Termination Request

Page 30: How To Think In Go

// Send request to terminate loop time.AfterFunc(5 * time.Second, func() { incomingCh <-struct{}{} }) <-exitCh // Wait for something to happen }

Go: Accept Termination Request

Page 31: How To Think In Go

! Explicit coordination required ! No goroutine specific storage

Goroutine

Page 32: How To Think In Go

! You must explicitly bail out for infinite loops in goroutines

One more thing:

Page 33: How To Think In Go

! Still, goroutines are worth it ! Channels can do much more

Too much hassle?

Page 34: How To Think In Go

Designing Structures

Page 35: How To Think In Go

“Structs and methods will allow us to make our Object-oriented code easy to port”

Misconception

Page 36: How To Think In Go
Page 37: How To Think In Go

Worker::Base

Worker::Foo Worker::Foo Worker::Foo

Page 38: How To Think In Go

package Worker::Base; # snip sub foo { # do stuff.. shift->something_overridable_in_child_class(); }

sub something_overridable_in_child_class { … }

Perl: Interaction Between Parent/Child

Page 39: How To Think In Go

package Worker::Foo; # snip use parent qw(Worker::Base); sub something_overridable_in_child_class { … } sub work { my $self = shift; while (1) { # do stuff… $self->foo(); # Doesn’t translate very well into Go } }

Perl: Interaction Between Parent/Child

Page 40: How To Think In Go
Page 41: How To Think In Go

! No. Just No. ! Embedded structs + Automatic Delegation

Go: No Inheritance

Page 42: How To Think In Go

type Name string func (n Name) Greet() string { return fmt.Sprintf(“Hello, my name is %s”, n) }

Go: Name

Page 43: How To Think In Go

n := Name(“Daisuke Maki”) println(n.Greet()) // “Hello, my name is Daisuke Maki”

Go: Name

Page 44: How To Think In Go

type Person struct { Name // Embedded struct Age uint // A regular field }

Go: Person

Page 45: How To Think In Go

p := &Person{ Name: Name(“Daisuke Maki”), Age: 38 }

println(p.Greet()) // “Hello, my name is Daisuke Maki”

Go: Automatic Delegation

Page 46: How To Think In Go

p.Greet() // → p.Name.Greet() // The receiver is p.Name, not p.

Go: Automatic Delegation

Page 47: How To Think In Go

func (p Person) Greet() string { return fmt.Sprintf( “%s. I’m %d years old”, p.Super.Greet(), // Pseudo-code. Doesn’t work p.Age, ) }

Go: Customizing Person.Greet()

Page 48: How To Think In Go

type BaseWorker struct {} func (w BaseWorker) Foo() { // This only class BaseWorker.SomethingOverridableInChildClass() w.SomethingOverridableInChildClass() }

type FooWorker struct { BaseWorker } func (w FooWorker) Work() { for { // Do interesting stuff… w.Foo() // w.BaseWorker.Foo(), receiver is never FooWorker } }

Go: Another Failed Attempt

Page 49: How To Think In Go

Wrong Approach: Top Down

Abstract Base Class

Concrete Implementation

Specialized Implementation

Page 50: How To Think In Go

Suggested Approach: Bottom Up

Type A

Component 1

Component 2

Component 3

Component 4

Type B Type C

Page 51: How To Think In Go

! Interfaces ! Promise that a type has certain

methods

Go: Grouping Types

Page 52: How To Think In Go

type Greeter interface { Greet() string }

func main() { p := &Person{ Name: “Mary Jane”, Age: 30 } n := Name(“John Doe”) greeters := []Greeters{ p, n } … }

Go: Things that can Greet()

Page 53: How To Think In Go

func sayHello(g Greeter) {     println(g.Greet()) }

for _, g := range greeters {     sayHello(g) }

Go: Things that can Greet()

Page 54: How To Think In Go

! Think in terms of ability (methods) ! But note: no “base” method

implementations

Go: Interfaces

Page 55: How To Think In Go

// WRONG! No methods for interfaces func (g Greeter) SayHello() {     println(g.Greet()) }

Go: No “base” methods

Page 56: How To Think In Go

// OK. Functions that take interfaces work func sayHello(g Greeter) { println(g.Greet()) }

// Each type would have to make this call func (n Name) SayHello() {     sayHello(n) // Name is a Greeter }

func (p Person) SayHello() { sayHello(n) // And so is Person, through delegation to p.Name }

Go: No “base” methods

Page 57: How To Think In Go

! Think of abilities, not Is-A, Has-A ! Compose from smaller components

Go: Designing Models

Page 58: How To Think In Go

! Don’t Use Exception: Use errors

Conclusions

Page 59: How To Think In Go

! Don’t Expect Goroutines = Threads/Processes

Conclusions

Page 60: How To Think In Go

! Don’t Design Using Tree-style hierarchy ! Create layers of standalone functionalities ! Compose them ! Use interfaces

Conclusions

Page 61: How To Think In Go

! (If you haven’t already heard) When In Go, Do As The Gophers Do

Conclusions

Page 62: How To Think In Go

Thank YouFurther reading:

! https://talks.golang.org/2014/readability.slide ! https://talks.golang.org/2014/gocon-tokyo.slide ! https://talks.golang.org/2013/bestpractices.slide ! http://blog.golang.org/errors-are-values