github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2014/go4gophers.slide (about) 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 [[http://godoc.org][godoc.org]] 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