Text file
talks/2014/go4gophers.slide
1 Go for gophers
2 GopherCon closing keynote
3 25 Apr 2014
4
5 Andrew Gerrand
6 Google, Inc.
7 @enneff
8 adg@golang.org
9
10
11 * Video
12
13 A video of this talk was recorded at GopherCon in Denver.
14
15 .link https://www.youtube.com/watch?v=dKGmK_Z1Zl0 Watch the talk on YouTube
16
17
18 * About me
19
20 .image go4gophers/gopherswim.jpg
21
22 I joined Google and the Go team in February 2010.
23
24 Had to re-think some of my preconceptions about programming.
25
26 Let me share what I have learned since.
27
28
29 * Interfaces
30
31
32 * Interfaces: first impressions
33
34 I used to think about classes and types.
35
36 Go resists this:
37
38 - No inheritance.
39 - No subtype polymorphism.
40 - No generics.
41
42 It instead emphasizes _interfaces_.
43
44
45 * Interfaces: the Go way
46
47 Go interfaces are small.
48
49 type Stringer interface {
50 String() string
51 }
52
53 A `Stringer` can pretty print itself.
54 Anything that implements `String` is a `Stringer`.
55
56
57 * An interface example
58
59 An `io.Reader` value emits a stream of binary data.
60
61 type Reader interface {
62 Read([]byte) (int, error)
63 }
64
65 Like a UNIX pipe.
66
67
68 * Implementing interfaces
69
70 .code go4gophers/reader.go /ByteReader/,/^}/
71
72
73 * Wrapping interfaces
74
75 .code go4gophers/reader.go /LogReader/,/STOP/
76
77 Wrapping a `ByteReader` with a `LogReader`:
78
79 .play go4gophers/reader.go /START/,/STOP/
80
81 By wrapping we compose interface _values_.
82
83
84 * Chaining interfaces
85
86 Wrapping wrappers to build chains:
87
88 .code go4gophers/chain.go /START/,/STOP/
89
90 More succinctly:
91
92 .play go4gophers/chain.go /LogReader{io/
93
94 Implement complex behavior by composing small pieces.
95
96
97 * Programming with interfaces
98
99 Interfaces separate data from behavior.
100
101 With interfaces, functions can operate on _behavior:_
102
103 // Copy copies from src to dst until either EOF is reached
104 // on src or an error occurs. It returns the number of bytes
105 // copied and the first error encountered while copying, if any.
106 func Copy(dst Writer, src Reader) (written int64, err error) {
107
108 .play go4gophers/chain.go /LogReader{io/
109
110 `Copy` can't know about the underlying data structures.
111
112
113 * A larger interface
114
115 `sort.Interface` describes the operations required to sort a collection:
116
117 type Interface interface {
118 Len() int
119 Less(i, j int) bool
120 Swap(i, j int)
121 }
122
123 `IntSlice` can sort a slice of ints:
124
125 type IntSlice []int
126
127 func (p IntSlice) Len() int { return len(p) }
128 func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
129 func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
130
131 `sort.Sort` uses can sort a `[]int` with `IntSlice`:
132
133 .play go4gophers/sort.go /START/,/STOP/
134
135
136 * Another interface example
137
138 The `Organ` type describes a body part and can print itself:
139
140 .play go4gophers/organs.go /type Organ/,$
141
142
143 * Sorting organs
144
145 The `Organs` type knows how to describe and mutate a slice of organs:
146
147 .code go4gophers/organs2.go /PART1/,/PART2/
148
149 The `ByName` and `ByWeight` types embed `Organs` to sort by different fields:
150
151 .code go4gophers/organs2.go /PART2/,/PART3/
152
153 With embedding we compose _types_.
154
155
156 * Sorting organs (continued)
157
158 To sort a `[]*Organ`, wrap it with `ByName` or `ByWeight` and pass it to `sort.Sort`:
159
160 .play go4gophers/organs2.go /START/,/STOP/
161
162
163 * Another wrapper
164
165 The `Reverse` function takes a `sort.Interface` and
166 returns a `sort.Interface` with an inverted `Less` method:
167
168 .code go4gophers/organs3.go /func Reverse/,$
169
170 To sort the organs in descending order, compose our sort types with `Reverse`:
171
172 .play go4gophers/organs3.go /START/,/STOP/
173
174
175 * Interfaces: why they work
176
177 These are not just cool tricks.
178
179 This is how we structure programs in Go.
180
181
182 * Interfaces: Sigourney
183
184 Sigourney is a modular audio synthesizer I wrote in Go.
185
186 .image go4gophers/sigourney.png
187
188 Audio is generated by a chain of `Processors`:
189
190 type Processor interface {
191 Process(buffer []Sample)
192 }
193
194 ([[https://github.com/nf/sigourney][github.com/nf/sigourney]])
195
196
197 * Interfaces: Roshi
198
199 Roshi is a time-series event store written by Peter Bourgon. It provides this API:
200
201 Insert(key, timestamp, value)
202 Delete(key, timestamp, value)
203 Select(key, offset, limit) []TimestampValue
204
205 The same API is implemented by the `farm` and `cluster` parts of the system.
206
207 .image go4gophers/roshi.png
208
209 An elegant design that exhibits composition.
210 ([[https://github.com/soundcloud/roshi][github.com/soundcloud/roshi]])
211
212
213 * Interfaces: why they work (continued)
214
215 Interfaces are _the_ generic programming mechanism.
216
217 This gives all Go code a familiar shape.
218
219 Less is more.
220
221
222 * Interfaces: why they work (continued)
223
224 It's all about composition.
225
226 Interfaces—by design and convention—encourage us to write composable code.
227
228
229 * Interfaces: why they work (continued)
230
231 Interfaces types are just types
232 and interface values are just values.
233
234 They are orthogonal to the rest of the language.
235
236
237 * Interfaces: why they work (continued)
238
239 Interfaces separate data from behavior. (Classes conflate them.)
240
241 type HandlerFunc func(ResponseWriter, *Request)
242
243 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
244 f(w, r)
245 }
246
247
248 * Interfaces: what I learned
249
250 Think about composition.
251
252 Better to have many small simple things than one big complex thing.
253
254 Also: what I thought of as small is pretty big.
255
256 Some repetition in the small is okay when it benefits "the large".
257
258
259 * Concurrency
260
261
262 * Concurrency: first impressions
263
264 My first exposure to concurrency was in C, Java, and Python.
265 Later: event-driven models in Python and JavaScript.
266
267 When I saw Go I saw:
268
269 "The performance of an event-driven model without callback hell."
270
271 But I had questions: "Why can't I wait on or kill a goroutine?"
272
273
274 * Concurrency: the Go way
275
276 Goroutines provide concurrent execution.
277
278 Channels express the communication and synchronization of independent processes.
279
280 Select enables computation on channel operations.
281
282 .image go4gophers/gopherflag.png
283
284
285 * A concurrency example
286
287 The binary tree comparison exercise from the Go Tour.
288
289 "Implement a function
290
291 func Same(t1, t2 *tree.Tree) bool
292
293 that compares the contents of two binary trees."
294
295 .image go4gophers/tree.png
296
297
298 * Walking a tree
299
300 type Tree struct {
301 Left, Right *Tree
302 Value int
303 }
304
305 A simple depth-first tree traversal:
306
307 .play go4gophers/tree-walk.go /func Walk/,$
308
309
310 * Comparing trees (1/2)
311
312 A concurrent walker:
313
314 .code go4gophers/tree-thread.go /func Walk/,/STOP/
315
316
317 * Comparing trees (2/2)
318
319 Walking two trees concurrently:
320
321 .play go4gophers/tree-thread.go /func Same/,$
322
323
324 * Comparing trees without channels (1/3)
325
326 .code go4gophers/tree-nothread.go /func Same/,/^}/
327
328 The `Walk` function has nearly the same signature:
329
330 .code go4gophers/tree-nothread.go /func Walk/
331 .code go4gophers/tree-nothread.go /func.+Next/
332
333 (We call `Next` instead of the channel receive.)
334
335
336 * Comparing trees without channels (2/3)
337
338 But the implementation is much more complex:
339
340 .code go4gophers/tree-nothread.go /func Walk/,/CUT/
341
342
343 * Comparing trees without channels (3/3)
344
345 .code go4gophers/tree-nothread.go /CUT/,/STOP/
346
347
348 * Another look at the channel version
349
350 .code go4gophers/tree-thread.go /func Walk/,/STOP/
351
352 But there's a problem: when an inequality is found,
353 a goroutine might be left blocked sending to `ch`.
354
355
356 * Stopping early
357
358 Add a `quit` channel to the walker so we can stop it mid-stride.
359
360 .code go4gophers/tree-select.go /func Walk/,/STOP/
361
362
363 * Stopping early (continued)
364
365 Create a `quit` channel and pass it to each walker.
366 By closing `quit` when the `Same` exits, any running walkers are terminated.
367
368 .code go4gophers/tree-select.go /func Same/,/^}/
369
370
371 * Why not just kill the goroutines?
372
373 Goroutines are invisible to Go code. They can't be killed or waited on.
374
375 You have to build that yourself.
376
377 There's a reason:
378
379 As soon as Go code knows in which thread it runs you get thread-locality.
380
381 Thread-locality defeats the concurrency model.
382
383
384 * Concurrency: why it works
385
386 The model makes concurrent code easy to read and write.
387 (Makes concurrency is *accessible*.)
388
389 This encourages the decomposition of independent computations.
390
391
392 * Concurrency: why it works (continued)
393
394 The simplicity of the concurrency model makes it flexible.
395
396 Channels are just values; they fit right into the type system.
397
398 Goroutines are invisible to Go code; this gives you concurrency anywhere.
399
400 Less is more.
401
402
403 * Concurrency: what I learned
404
405 Concurrency is not just for doing more things faster.
406
407 It's for writing better code.
408
409
410 * Syntax
411
412
413 * Syntax: first impressions
414
415 At first, Go syntax felt a bit inflexible and verbose.
416
417 It affords few of the conveniences to which I was accustomed.
418
419 For instance:
420
421 - No getters/setters on fields.
422 - No map/filter/reduce/zip.
423 - No optional arguments.
424
425
426 * Syntax: the Go way
427
428 Favor readability above all.
429
430 Offer enough sugar to be productive, but not too much.
431
432
433 * Getters and setters (or "properties")
434
435 Getters and setters turn assignments and reads into function calls.
436 This leads to surprising hidden behavior.
437
438 In Go, just write (and call) the methods.
439
440 The control flow cannot be obscured.
441
442
443 * Map/filter/reduce/zip
444
445 Map/filter/reduce/zip are useful in Python.
446
447 a = [1, 2, 3, 4]
448 b = map(lambda x: x+1, a)
449
450 In Go, you just write the loops.
451
452 a := []int{1, 2, 3, 4}
453 b := make([]int, len(a))
454 for i, x := range a {
455 b[i] = x+1
456 }
457
458 This is a little more verbose,
459 but makes the performance characteristics obvious.
460
461 It's easy code to write, and you get more control.
462
463
464 * Optional arguments
465
466 Go functions can't have optional arguments.
467
468 Instead, use variations of the function:
469
470 func NewWriter(w io.Writer) *Writer
471 func NewWriterLevel(w io.Writer, level int) (*Writer, error)
472
473 Or an options struct:
474
475 func New(o *Options) (*Jar, error)
476
477 type Options struct {
478 PublicSuffixList PublicSuffixList
479 }
480
481 Or a variadic list of options.
482
483 Create many small simple things, not one big complex thing.
484
485
486 * Syntax: why it works
487
488 The language resists convoluted code.
489
490 With obvious control flow, it's easy to navigate unfamiliar code.
491
492 Instead we create more small things that are easy to document and understand.
493
494 So Go code is easy to read.
495
496 (And with gofmt, it's easy to write readable code.)
497
498
499 * Syntax: what I learned
500
501 I was often too clever for my own good.
502
503 I appreciate the consistency, clarity, and _transparency_ of Go code.
504
505 I sometimes miss the conveniences, but rarely.
506
507
508 * Error handling
509
510
511 * Error handling: first impressions
512
513 I had previously used exceptions to handle errors.
514
515 Go's error handling model felt verbose by comparison.
516
517 I was immediately tired of typing this:
518
519 if err != nil {
520 return err
521 }
522
523
524 * Error handling: the Go way
525
526 Go codifies errors with the built-in `error` interface:
527
528 type error interface {
529 Error() string
530 }
531
532 Error values are used just like any other value.
533
534 func doSomething() error
535
536 err := doSomething()
537 if err != nil {
538 log.Println("An error occurred:", err)
539 }
540
541 Error handling code is just code.
542
543 (Started as a convention (`os.Error`). We made it built in for Go 1.)
544
545
546 * Error handling: why it works
547
548 Error handling is important.
549
550 Go makes error handling as important as any other code.
551
552
553 * Error handling: why it works (continued)
554
555 Errors are just values; they fit easily into the rest of the language
556 (interfaces, channels, and so on).
557
558 Result: Go code handles errors correctly and elegantly.
559
560
561 * Error handling: why it works (continued)
562
563 We use the same language for errors as everything else.
564
565 Lack of hidden control flow (throw/try/catch/finally) improves readability.
566
567 Less is more.
568
569
570
571 * Error handling: what I learned
572
573 To write good code we must think about errors.
574
575 Exceptions make it easy to avoid thinking about errors.
576 (Errors shouldn't be "exceptional!")
577
578 Go encourages us to consider every error condition.
579
580 My Go programs are far more robust than my programs in other languages.
581
582 I don't miss exceptions at all.
583
584
585 * Packages
586
587
588 * Packages: first impressions
589
590 I found the capital-letter-visibility rule weird;
591 "Let me use my own naming scheme!"
592
593 I didn't like "package per directory";
594 "Let me use my own structure!"
595
596 I was disappointed by lack of monkey patching.
597
598
599 * Packages: the Go way
600
601 Go packages are a name space for types, functions, variables, and constants.
602
603
604 * Visibility
605
606 Visibility is at the package level.
607 Names are "exported" when they begin with a capital letter.
608
609 package zip
610
611 func NewReader(r io.ReaderAt, size int64) (*Reader, error) // exported
612
613 type Reader struct { // exported
614 File []*File // exported
615 Comment string // exported
616 r io.ReaderAt // unexported
617 }
618
619 func (f *File) Open() (rc io.ReadCloser, err error) // exported
620
621 func (f *File) findBodyOffset() (int64, error) // unexported
622
623 func readDirectoryHeader(f *File, r io.Reader) error // unexported
624
625 Good for readability: easy to see whether a name is part of the public interface.
626 Good for design: couples naming decisions with interface decisions.
627
628
629 * Package structure
630
631 Packages can be spread across multiple files.
632
633 Permits shared private implementation and informal code organization.
634
635 Packages files must live in a directory unique to the package.
636
637 The path to that directory determines the package's import path.
638
639 The build system locates dependencies from the source alone.
640
641
642 * "Monkey patching"
643
644 Go forbids modifying package declarations from outside the package.
645
646 But we can get similar behavior using global variables:
647
648 package flag
649
650 var Usage = func() {
651 fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
652 PrintDefaults()
653 }
654
655 Or registration functions:
656
657 package http
658
659 func Handle(pattern string, handler Handler)
660
661 This gives the flexibility of monkey patching but on the package author's terms.
662
663 (This depends on Go's initialization semantics.)
664
665
666 * Packages: why they work
667
668 The loose organization of packages lets us write and refactor code quickly.
669
670 But packages encourage the programmer to consider the public interface.
671
672 This leads to good names and simpler interfaces.
673
674 With the source as the single source of truth,
675 there are no makefiles to get out of sync.
676
677 (This design enables great tools like [[https://pkg.go.dev][pkg.go.dev]] and goimports.)
678
679 Predictable semantics make packages easy to read, understand, and use.
680
681
682 * Packages: what I learned
683
684 Go's package system taught me to prioritize the consumer of my code.
685 (Even if that consumer is me.)
686
687 It also stopped me from doing gross stuff.
688
689 Packages are rigid where it matters, and loose where it doesn't.
690 It just feels right.
691
692 Probably my favorite part of the language.
693
694
695 * Documentation
696
697
698 * Documentation: first impressions
699
700 Godoc reads documentation from Go source code, like `pydoc` or `javadoc`.
701
702 But unlike those two, it doesn't support complex formatting or other meta data.
703 Why?
704
705
706 * Documentation: the Go way
707
708 Godoc comments precede the declaration of an exported identifier:
709
710 // Join concatenates the elements of a to create a single string.
711 // The separator string sep is placed between elements in the resulting string.
712 func Join(a []string, sep string) string {
713
714 It extracts the comments and presents them:
715
716 $ godoc strings Join
717 func Join(a []string, sep string) string
718 Join concatenates the elements of a to create a single string. The
719 separator string sep is placed between elements in the resulting string.
720
721 Also integrated with the testing framework to provide testable example functions.
722
723 func ExampleJoin() {
724 s := []string{"foo", "bar", "baz"}
725 fmt.Println(strings.Join(s, ", "))
726 // Output: foo, bar, baz
727 }
728
729
730 * Documentation: the Go way (continued)
731
732 .image go4gophers/godoc.png
733
734
735 * Documentation: why it works
736
737 Godoc wants you to write good comments, so the source looks great:
738
739 // ValidMove reports whether the specified move is valid.
740 func ValidMove(from, to Position) bool
741
742 Javadoc just wants to produce pretty documentation, so the source is hideous:
743
744 /**
745 * Validates a chess move.
746 *
747 * @param fromPos position from which a piece is being moved
748 * @param toPos position to which a piece is being moved
749 * @return true if the move is valid, otherwise false
750 */
751 boolean isValidMove(Position fromPos, Position toPos)
752
753 (Also a grep for `"ValidMove"` will return the first line of documentation.)
754
755
756 * Documentation: what I learned
757
758 Godoc taught me to write documentation _as_I_code._
759
760 Writing documentation _improves_the_code_ I write.
761
762
763 * More
764
765 There are many more examples.
766
767 The overriding theme:
768
769 - At first, something seemed weird or lacking.
770 - I realized it was a design decision.
771
772 Those decisions make the language—and Go code—better.
773
774 Sometimes you have to live with the language a while to see it.
775
776
777 * Lessons
778
779
780 * Code is communication
781
782 Be articulate:
783
784 - Choose good names.
785 - Design simple interfaces.
786 - Write precise documentation.
787 - Don't be too clever.
788
789
790 * Less is exponentially more
791
792 New features can weaken existing features.
793
794 Features multiply complexity.
795
796 Complexity defeats orthogonality.
797
798 Orthogonality is vital: it enables composition.
799
800
801 * Composition is key
802
803 Don't solve problems by building _a_ thing.
804
805 Instead, combine simple tools and compose them.
806
807
808 * Design good interfaces
809
810 .image go4gophers/gophertraining.png
811
812 .html go4gophers/gophertraining.html
813
814
815 * Simplicity is hard
816
817 Invest the time to find the simple solution.
818
819
820 * Go's effect on me
821
822 These lessons were all things I already "knew".
823
824 Go helped me internalize them.
825
826 .image go4gophers/gopherhat.jpg
827
828 Go made me a better programmer.
829
830
831 * A message for gophers everywhere
832
833 Let's build small, simple, and beautiful things together.
834
835 .image go4gophers/gopherswrench.jpg
836
837
View as plain text