How To Think In Go
or, let me tell you about all the ways I screwed up
https://www.flickr.com/photos/spam/3355824586
Panics and Errors
“Oh look, panic() and rescue()! I can
probably use it like lightweight
exceptions!”
Misconception
! panic: unrecoverable errors
! error: recoverable errors
panic / error
sub foo {
eval {
might_die();
};
if ($@) {
# handle $@
}
important_task();
}
Perl: Exceptions
func main() {
defer func() {
if err := recover(); err != nil {
// handle err
}
}()
mightPanic()
importantTask() // Never reached if mightPanic() panics
}
A bad port
func mightPanic() {
defer func() {
if err := recover(); err != nil {
// handle err
}
}()
panic(“BOO!”)
}
func main() {
mightPanic()
importantTask()
}
Attempt #2
func main() {
if err := tryit(mightPanic); err != nil {
fmt.Printf(“Found error: %sn”, err)
}
importantTask()
}
func mightPanic() {
panic(“BOO!”)
}
Attempt #3
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
“Why don’t I just use plain errors to
begin with?”
Go Way
func mightError() error {
     if errorCase {
          return errors.New(“error case 1”)
     }
     if err := someOtherThingMightError(); err != nil {
          // propagate error
          return err
     }
     return nil
}
Use errors!
func main() {
     if err := mightError(); err != nil {
         // handle it
     }
     importantTask()
}
Use errors!
! Do not dream about exceptions
! Do stick with errors
Use errors!
Concurrency
“Go’s makes concurrency so easy, we
can just port our multi-process code in
an instant!”
Misconception
! PIDs to identify processes
! Processes can be signaled
! Processes can notify termination
Processes
! No pid to identify goroutines
! No signals to communicate
! Goroutines don’t notify on exit
Goroutine
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 $pidn”;
     };
     sleep 5;
     kill TERM => $pid;
     while (kill 0 => $pid) { sleep 1 }
}
Perl: Processes
func main() { // Won’t work
    go func() {
for {
doStuff()
}
}
}
Go: A simple goroutine
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
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
// Send request to terminate loop
time.AfterFunc(5 * time.Second, func() {
incomingCh <-struct{}{}
})
<-exitCh // Wait for something to happen
}
Go:Accept Termination Request
! Explicit coordination required
! No goroutine specific storage
Goroutine
! You must explicitly bail out for
infinite loops in goroutines
One more thing:
! Still, goroutines are worth it
! Channels can do much more
Too much hassle?
Designing Structures
“Structs and methods will allow us to
make our Object-oriented code easy to
port”
Misconception
Worker::Base
Worker::Foo Worker::Foo Worker::Foo
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
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
! No. Just No.
! Embedded structs + Automatic Delegation
Go: No Inheritance
type Name string
func (n Name) Greet() string {
return fmt.Sprintf(“Hello, my name is %s”, n)
}
Go: Name
n := Name(“Daisuke Maki”)
println(n.Greet()) // “Hello, my name is Daisuke Maki”
Go: Name
type Person struct {
Name // Embedded struct
Age uint // A regular field
}
Go: Person
p := &Person{
Name: Name(“Daisuke Maki”),
Age: 38
}
println(p.Greet()) // “Hello, my name is Daisuke Maki”
Go: Automatic Delegation
p.Greet() // → p.Name.Greet()
// The receiver is p.Name, not p.
Go: Automatic Delegation
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()
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
Wrong Approach:Top Down
Abstract Base Class
Concrete Implementation
Specialized Implementation
…
…
Suggested Approach: Bottom Up
Type A
Component 1
Component 2
Component 3
Component 4
Type B Type C
! Interfaces
! Promise that a type has certain
methods
Go: Grouping Types
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()
func sayHello(g Greeter) {
    println(g.Greet())
}
for _, g := range greeters {
    sayHello(g)
}
Go:Things that can Greet()
! Think in terms of ability (methods)
! But note: no “base” method
implementations
Go: Interfaces
// WRONG! No methods for interfaces
func (g Greeter) SayHello() {
    println(g.Greet())
}
Go: No “base” methods
// 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
! Think of abilities, not Is-A, Has-A
! Compose from smaller components
Go: Designing Models
! Don’t Use Exception: Use errors
Conclusions
! Don’t Expect Goroutines = Threads/
Processes
Conclusions
! Don’t Design Using Tree-style hierarchy
! Create layers of standalone functionalities
! Compose them
! Use interfaces
Conclusions
! (If you haven’t already heard) 

When In Go, Do As The Gophers Do
Conclusions
Thank You
Further 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

How To Think In Go

  • 1.
    How To ThinkIn Go or, let me tell you about all the ways I screwed up
  • 6.
  • 7.
  • 8.
    “Oh look, panic()and rescue()! I can probably use it like lightweight exceptions!” Misconception
  • 10.
    ! panic: unrecoverableerrors ! error: recoverable errors panic / error
  • 11.
    sub foo { eval{ might_die(); }; if ($@) { # handle $@ } important_task(); } Perl: Exceptions
  • 12.
    func main() { deferfunc() { if err := recover(); err != nil { // handle err } }() mightPanic() importantTask() // Never reached if mightPanic() panics } A bad port
  • 13.
    func mightPanic() { deferfunc() { if err := recover(); err != nil { // handle err } }() panic(“BOO!”) } func main() { mightPanic() importantTask() } Attempt #2
  • 14.
    func main() { iferr := tryit(mightPanic); err != nil { fmt.Printf(“Found error: %sn”, err) } importantTask() } func mightPanic() { panic(“BOO!”) } Attempt #3
  • 15.
    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
  • 17.
    “Why don’t Ijust use plain errors to begin with?” Go Way
  • 18.
    func mightError() error{      if errorCase {           return errors.New(“error case 1”)      }      if err := someOtherThingMightError(); err != nil {           // propagate error           return err      }      return nil } Use errors!
  • 19.
    func main() {     if err := mightError(); err != nil {          // handle it      }      importantTask() } Use errors!
  • 20.
    ! Do notdream about exceptions ! Do stick with errors Use errors!
  • 21.
  • 22.
    “Go’s makes concurrencyso easy, we can just port our multi-process code in an instant!” Misconception
  • 24.
    ! PIDs toidentify processes ! Processes can be signaled ! Processes can notify termination Processes
  • 25.
    ! No pidto identify goroutines ! No signals to communicate ! Goroutines don’t notify on exit Goroutine
  • 26.
    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 $pidn”;      };      sleep 5;      kill TERM => $pid;      while (kill 0 => $pid) { sleep 1 } } Perl: Processes
  • 27.
    func main() {// Won’t work     go func() { for { doStuff() } } } Go: A simple goroutine
  • 28.
    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
  • 29.
    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
  • 30.
    // Send requestto terminate loop time.AfterFunc(5 * time.Second, func() { incomingCh <-struct{}{} }) <-exitCh // Wait for something to happen } Go:Accept Termination Request
  • 31.
    ! Explicit coordinationrequired ! No goroutine specific storage Goroutine
  • 32.
    ! You mustexplicitly bail out for infinite loops in goroutines One more thing:
  • 33.
    ! Still, goroutinesare worth it ! Channels can do much more Too much hassle?
  • 34.
  • 35.
    “Structs and methodswill allow us to make our Object-oriented code easy to port” Misconception
  • 37.
  • 38.
    package Worker::Base; # snip subfoo { # do stuff.. shift->something_overridable_in_child_class(); } sub something_overridable_in_child_class { … } Perl: Interaction Between Parent/Child
  • 39.
    package Worker::Foo; # snip useparent 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
  • 41.
    ! No. JustNo. ! Embedded structs + Automatic Delegation Go: No Inheritance
  • 42.
    type Name string func(n Name) Greet() string { return fmt.Sprintf(“Hello, my name is %s”, n) } Go: Name
  • 43.
    n := Name(“DaisukeMaki”) println(n.Greet()) // “Hello, my name is Daisuke Maki” Go: Name
  • 44.
    type Person struct{ Name // Embedded struct Age uint // A regular field } Go: Person
  • 45.
    p := &Person{ Name:Name(“Daisuke Maki”), Age: 38 } println(p.Greet()) // “Hello, my name is Daisuke Maki” Go: Automatic Delegation
  • 46.
    p.Greet() // →p.Name.Greet() // The receiver is p.Name, not p. Go: Automatic Delegation
  • 47.
    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()
  • 48.
    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
  • 49.
    Wrong Approach:Top Down AbstractBase Class Concrete Implementation Specialized Implementation … …
  • 50.
    Suggested Approach: BottomUp Type A Component 1 Component 2 Component 3 Component 4 Type B Type C
  • 51.
    ! Interfaces ! Promisethat a type has certain methods Go: Grouping Types
  • 52.
    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()
  • 53.
    func sayHello(g Greeter){     println(g.Greet()) } for _, g := range greeters {     sayHello(g) } Go:Things that can Greet()
  • 54.
    ! Think interms of ability (methods) ! But note: no “base” method implementations Go: Interfaces
  • 55.
    // WRONG! Nomethods for interfaces func (g Greeter) SayHello() {     println(g.Greet()) } Go: No “base” methods
  • 56.
    // OK. Functionsthat 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
  • 57.
    ! Think ofabilities, not Is-A, Has-A ! Compose from smaller components Go: Designing Models
  • 58.
    ! Don’t UseException: Use errors Conclusions
  • 59.
    ! Don’t ExpectGoroutines = Threads/ Processes Conclusions
  • 60.
    ! Don’t DesignUsing Tree-style hierarchy ! Create layers of standalone functionalities ! Compose them ! Use interfaces Conclusions
  • 61.
    ! (If youhaven’t already heard) 
 When In Go, Do As The Gophers Do Conclusions
  • 62.
    Thank You Further 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