Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go's opinions

Go's opinions

chinglin

April 18, 2019
Tweet

More Decks by chinglin

Other Decks in Programming

Transcript

  1. Kubernetes & Go https://www.cncf.io/about/members/ NetEase reports that their internal cloud

    platform supports 30,000 nodes in a single cluster. Moving to Kubernetes has increased their R&D efficiency by more than 100% and deployment efficiency by 280%. [link] JD currently runs the world’s largest Kubernetes cluster in production. [link] • 77,126 commits made by 2,561 contributors • 1,307,371 lines of code • mostly written in Go
  2. What is Go Go is expressive, concise, clean, and efficient.

    Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, Strong type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language. https://golang.org/doc/
  3. Go’s traits • Go is cross-platform, so you can target

    an operating system of your choice when compiling a piece of code. • Go offers a native concurrency model that is unlike most mainstream programming languages. Go relies on a concurrency model called CSP ( Communicating Sequential Processes). Instead of locking variables to share memory, • Go has a fairly mature package of its own. • Go code typically compiles to a single native binary. • Go is also being rapidly being adopted as the go-to cloud native language.
  4. Go & OOP Go intentionally leaves out many features of

    modern OOP languages. • No classes. Everything is divided into packages only. Go has only structs instead of classes. • Does not support inheritance. That will make code easy to modify. In other languages like Java/Python, if the class ABC inherits class XYZ and you make some changes in class XYZ, then that may produce some side effects in other classes that inherit XYZ. • By removing inheritance, Go makes it easy to understand the code also (as there is no super class to look at while looking at a piece of code). We will come back to OOP later...
  5. Dependencies "dependencies" are anything your program expects to exist in

    its execution environment. This could include: • The operating system on which the app runs • The VM or interpreter which runs the code • Packages which are imported in the code Go handle dependency at build time, not in the hands of the user. GOOS=windows GOARCH=386 go build -o hello.exe hello.go GOOS=darwin GOARCH=amd64 go build hello.go
  6. Hello world package main import ( "fmt" "net/http" ) func

    handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s.", r.URL.Path[1:]) } func main() { http.ListenAndServe(":8080", http.HandlerFunc(handler)) }
  7. Robert Griesemer Google's V8 JavaScript engine and Chubby, a distributed

    lock manager for Google's GFS distributed filesystem. He has also worked on the design and implementation of the domain-specific language Sawzall, the Java HotSpot virtual machine, and the Strongtalk system. He has also written a vectorizing compiler for the Cray Y-MP and an interpreter for APL. https://alchetron.com/Robert-Griesemer
  8. Go’s gene Go is a programming language designed by Google

    to help solve Google's problems, and Google has big problems. 1. The hardware is big and 2. the software is big. 3. Thousands of engineers work on the code And of course, all this software runs on zillions of machines, which are treated as a modest number of independent, networked compute clusters.
  9. Go package dependency at scale 1 In Go, programs compile

    into packages and each compiled package file imports transitive dependency info. If A.go depends on B.go depends on C.go: • compile C.go, B.go, then A.go. • to recompile A.go, compiler reads B.o but not C.o. At scale, this can be a huge speedup
  10. Go package dependency at scale 2 Large C++ programs (Firefox,

    OpenOffice, Chromium) have huge build times. On a Mac (OS X 10.5.7, gcc 4.0.1): • C: #include <stdio.h> ◦ reads 360 lines from 9 files • C++: #include <iostream> ◦ reads 25,326 lines from 131 files • Objective-C: #include <Cocoa/Cocoa.h> ◦ reads 112,047 lines from 689 files But we haven't done any real work yet! • In Go, import "fmt" reads one file: ◦ 195 lines summarizing 6 dependent packages As we scale, the improvement becomes exponential.
  11. Go concurrency Hardware is big means, there’s big distributed network

    connected system. Go concurrency makes manage connections and clients easier, and so performance. Go provides independently executing goroutines that communicate and synchronize using channels.
  12. Goroutines Start a new flow of control with the go

    keyword. Parallel computation is easy: func main() { go expensiveComputation(x, y, z) anotherExpensiveComputation(a, b, c) }
  13. Goroutine is like a thread, but lighter weight • stacks

    are small, segmented, sized on demand • goroutines are multiplexed by demand onto true threads • requires support from language, compiler, runtime • can't just be a C++ library
  14. Thread per connection Doesn't scale in practice, so in most

    languages we use event-driven callbacks and continuations. But in Go, a goroutine per connection model scales well. for { rw := socket.Accept() conn := newConn(rw, handler) go conn.serve() }
  15. Channels Our trivial parallel program again: func main() { go

    expensiveComputation(x, y, z) anotherExpensiveComputation(a, b, c) } • Need to know when the computations are done. • Need to know the result. A Go channel provides the capability: a typed synchronous communications mechanism.
  16. Goroutines communicate using channels func computeAndSend(x, y, z int) chan

    int { ch := make(chan int) go func() { ch <- expensiveComputation(x, y, z) }() return ch } func main() { ch := computeAndSend(x, y, z) v2 := anotherExpensiveComputation(a, b, c) v1 := <-ch fmt.Println(v1, v2) }
  17. Go built in tools • Go run • Go build

    • Go get • Go test • Go profile • Go mod https://www.alexedwards.net/blog/an-overview-of-go-tooling Especially gofmt makes single programming style, everyone’s code looks the same. Which makes writing relate tool easier.
  18. Go tools from community • gocode for auto-completion • go-outline

    for symbol search in the current file • go-symbols for symbol search in the current workspace • gopkgs for auto-completion of unimported packages • guru for the Find all References feature • gorename for renaming symbols • goreturns or goimports for formatting code • godef or gogetdoc for the Go to Definition feature • godoc or gogetdoc for the documentation that appears on hover • golint or gometalinter or megacheck or golangci-lint or revive for linting • dlv for debugging
  19. Go’s gene2 Go's purpose is therefore not to do research

    into programming language design; it is to improve the working environment for its designers and their coworkers. Go is more about software engineering than programming language research. Or to rephrase, it is about language design in the service of software engineering. But what is software engineering? [link] Software engineering is what happens to programming when you add time and other programmers.
  20. Go boring? Boring? If you’re a language nerd. Boring is

    exciting! If you like stability. Stable foundation, build stuff on top Excitement higher in the stack, and in non-language areas of the foundation Excitement besides the language… https://golang.org/doc/go1compat
  21. Go design Software engineering guided the design of Go. More

    than most general-purpose programming languages, Go was designed to address a set of software engineering issues that we had been exposed to in the construction of large server software. That might make Go sound rather dull and industrial, But in fact the focus on clarity, simplicity and composability throughout the design instead resulted in a productive, fun language that many programmers find expressive and powerful. We will come back to expressiveness later...
  22. Four opinions 1. Less is more 2. Go is about

    composition 3. Let machine do the job 4. Explicit errors handling
  23. Less-1 • no header files (faster build) • no circular

    dependencies • no box (constants are just numbers) • no public,private keywords (letter case sets visibility) • no un-used variable (compiling fail) • no memory management (manage automatically)
  24. Less-2 • methods for any type (no classes) • no

    subtype inheritance (no subclasses) • interfaces are implicit (no "implements" declaration) • embedding (no promotion to superclass) • methods are declared as functions (no special location) • methods are just functions (same receiver as first argument) • interfaces are just methods (no data, behavior contract only) • methods match by name only (not by type) • no constructors or destructors • no implicit "this" in methods
  25. Less-3 • no arithmetic conversions (constants help) • no pointer

    arithmetic • no random number (memory is always zeroed) • no const or other type annotations • no templates • no exceptions
  26. Less keywords Number of keywords is an approximate measure of

    complexity. * extra count is for reserved words and alternate spellings
  27. Expressiveness Go is: Simple • concepts are easy to understand

    • (the implementation might still be sophisticated) Orthogonal • concepts mix cleanly • easy to understand and predict what happens Succinct • no need to predeclare every intention Safe • misbehavior should be detected These combine to give expressiveness.
  28. Struct • Structs describe (and control) the layout of data.

    • The (p Point) declares the receiver (no automatic " this " • variable; also notice p is not a pointer, although it could be.) • Methods are not mixed with the data definition. • They are orthogonal to types. type Point struct { x, y float } func (p Point) Abs() float { return math.Sqrt(p.x*p.x + p.y*p.y) }
  29. Methods • Methods are orthogonal to types. • Orthogonality of

    methods allows any type to have them. • Orthogonality leads to generality. type Vector []float func (v Vector) Abs() float { sumOfSquares := 0.0 for i := range v { sumOfSquares += v[i]*v[i] } return math.Sqrt(sumOfSquares) }
  30. Interface • Interfaces are just sets of methods; work for

    any type. • Interfaces are satisfied implicitly. • Point and Vector do not declare that they implement Abser , they just do. type Abser interface { Abs() float } var a Abser a = Point{3, 4} print(a.Abs()) a = Vector{1, 2, 3, 4} print(a.Abs())
  31. Interface2 • Interfaces are abstract, other types are concrete. 1.

    concrete type such as structs define data 2. interfaces define behavior • As with methods, now anything can satisfy an interface. type Value float // basic type func (v Value) Abs() float { if v < 0 { v = -v } return float(v) } a = Value(-27) print(a.Abs())
  32. Expressiveness about type and data Simple • interfaces are just

    method sets Orthogonal • representation (data) and behavior (methods) are independent • empty interface can represent any value Succinct • no implements declarations; interfaces just satisfy Safe • static type checking Expressiveness: implicit satisfaction lets pieces fit together seamlessly.
  33. Expressiveness about visibility • Case of first character determines visibility

    outside package ◦ ThisNameIsPublic ◦ thisOneIsNot • simple, easy rule to understand consequences clear • see a variable, can see whether it's public without going to the declaration • can make any type, variable, or constant public or not with the same mechanism Orthogonality again!
  34. Concepts of concurrency • Goroutines ◦ A goroutine is a

    lightweight thread of execution • Channels • Stacks • Closures ◦ A closure is a function value that references variables from outside its body.
  35. Go concurrency basics recap Start a goroutine: go f() Channel

    send (arrow points in direction of flow): ch <- value Channel receive: value = <-ch Channels are unbuffered by default, which combines synchronization with communication.
  36. Expressiveness about concurrency and closure Simple • stacks just work;

    goroutines too cheap to meter Orthogonal • concurrency orthogonal to rest of language • orthogonality of functions make closures easy Succinct • go f() • closure syntax clear Safe • no stack overflows • garbage collection avoids many concurrency problems Expressiveness: complex behaviors easily expressed.
  37. More expressive than C or C++ type and data, name(visibility),

    concurrency(goroutine,channel) Expressiveness comes from orthogonal composition of constructs.
  38. Convergence and features • The talks at Lang.Next were about

    new language versions (Java 8, ECMAScript 6, C#, C++14) and were lists of features. • These languages evolve and compete by adding features. • The languages grow in complexity while becoming more similar. • Bloat without distinction.
  39. Features in Go • Go is different. ( somewhat less(

    fewer features), but enable more ) • Go does not try to be like the other languages. • Go does not compete on features. • As of Go 1, the language is fixed. • Many newcomers to Go ask for features from languages they know. • But those features do not belong in Go—and the language is fixed. • Adding features to Go would not make it better, just bigger.
  40. Go’s less opinions becomes simplicity Simplicity is hard—to design. Simplicity

    is complicated—to build. But if you get it right... Simplicity is easy—to use. The success of Go proves it.
  41. GC makes interfaces more simple Go has no explicit memory-freeing

    operation: the only way allocated memory returns to the pool is through the garbage collector. In C and C++, too much programming effort is spent on memory allocation and freeing. In a concurrent object-oriented language it's almost essential to have automatic memory management because the ownership of a piece of memory can be tricky to manage as it is passed around among concurrent executions. It's important to separate behavior from resource management. The language is much easier to use because of garbage collection.
  42. GC’s benefit outweigh the costs Of course, garbage collection brings

    significant costs: general overhead, latency, and complexity of the implementation. Nonetheless, we believe that the benefits, which are mostly felt by the programmer, outweigh the costs.
  43. GC controlling Go gives the programmer tools to limit allocation

    by controlling the layout of data structures. In Go it is easy and efficient to provide second-order allocators, for instance an arena allocator that allocates a large array of structs and links them together with a free list. Libraries that repeatedly use many small structures like this can, with modest prearrangement, generate no garbage yet be efficient and responsive. A knowledgeable programmer can limit the pressure placed on the collector and thereby improve performance.
  44. Control of bits and memory Like C, Go has •

    full set of unsigned types • bit-level operations • programmer control of memory layout type T struct { x int buf [20]byte ... } // pointers to inner values p := &t.buf
  45. Garbage collection Automatic memory management simplifies life. GC is critical

    for concurrent programming; otherwise it's too fussy and error-prone to track ownership as data moves around. C++ libraries is about deciding who owns memory, who destroys resources. Memory management must be hidden behind the interface. But garbage collection isn't enough.
  46. Memory safety Memory in Go is intrinsically safer: • pointers

    but no pointer arithmetic • no dangling pointers (locals move to heap as needed) • no pointer-to-integer conversions* • all variables are zero-initialized • all indexing is bounds-checked Should have far fewer buffer overflow exploits. • Package unsafe allows this but labels the code as dangerous; used mainly in some low-level libraries.
  47. If C++ and Java are about type hierarchies and the

    taxonomy of types Go is about composition.
  48. Programming paradigm Common programming paradigms include: imperative in which the

    programmer instructs the machine how to change its state, • procedural which groups instructions into procedures, • object-oriented which groups instructions together with the part of the state they operate on declarative in which the programmer merely declares properties of the desired result, but not how to compute it https://en.wikipedia.org/wiki/Programming_paradigm
  49. Type hierarchy Type hierarchies are just taxonomy. You need to

    decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. It's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.
  50. Composition not inheritance (no type hierarchy) Go takes an unusual

    approach to object-oriented programming, allowing methods on any type, not just classes. Without any form of type-based inheritance like subclassing. This means there is no type hierarchy. This was an intentional design choice.
  51. Type hierarchies result in brittle code The hierarchy must be

    designed early, often as the first step of designing the program, and early decisions can be difficult to change once the program is written. As a consequence, the model encourages early overdesign as the programmer tries to predict every possible use the software might require, adding layers of type and abstraction just in case. This is upside down. The way pieces of a system interact should adapt as it grows, not be fixed at the beginning.
  52. Go encourages composition over inheritance Compose by using: Simple, often

    one-method interfaces To define trivial behaviors That serve as clean, comprehensible boundaries between components.
  53. Go’s interface recap All data types that implement these methods

    satisfy this interface implicitly; there is no implements declaration Interface satisfaction is statically checked at compile time so despite this decoupling interfaces are type-safe.
  54. Composition example Consider the io.Writer interface Any item that has

    a Write method with this signature works well with the complementary Reader interface: type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
  55. Composition example ( chaining with rich behaviors ) The reader

    and writer methods allow type-safe chaining with rich behaviors, like generalized Unix pipes. Files, buffers, networks, encryptors, compressors, image encoders, and so on can all be connected together. The Fprintf formatted I/O routine takes an io.Writer rather than, as in C, a FILE*. The formatted printer has no knowledge of what it is writing to; It may be a image encoder that is in turn writing to a compressor that is in turn writing to an encryptor that is in turn writing to a network connection.
  56. Grow gracefully Note too that the elimination of the type

    hierarchy also eliminates a form of dependency hierarchy. Interface satisfaction allows the program to grow organically without predetermined contracts. And it is a linear form of growth; a change to an interface affects only the immediate clients of that interface; there is no subtree to update. The lack of implements declarations disturbs some people but it enables programs to grow naturally, gracefully, and safely.
  57. Composition makes Go scalable Here are some prototypes. LoggingReader logs

    every Read call on the incoming Reader.LimitingReader stops reading after n bytes. ErrorInjector aids testing by simulating I/O errors. And there are many more. • func LoggingReader(r io.Reader) io.Reader • func LimitingReader(r io.Reader, n int64) io.Reader • func ErrorInjector(r io.Reader) io.Reader The designs are nothing like hierarchical, subtype-inherited methods. They are looser (even ad hoc), organic, decoupled, independent, and therefore scalable.
  58. Language of composition and coupling Interfaces give us the composition

    of components. It doesn't matter what that thing is, if it implements method M I can just drop it in here. Another important example is how concurrency gives us the composition of independently executing computations. And there's even an unusual (and very simple) form of type composition: embedding.
  59. Compose to a solution to your problem • Go isn't

    all-encompassing. You don't get everything built in. You don't have precise control of every nuance of execution. For instance, you don't have RAII(constructor and destructor). Instead you get a garbage collector. You don't even get a memory-freeing function. • What you're given is a set of powerful but easy to understand, easy to use building blocks from which you can assemble—compose—a solution to your problem. • Compose method are easier to write, easier to read, easier to understand, easier to maintain, and maybe safer. ◦ No need to plan ahead ( as type hierarchy ) ◦ replace any component as you wish
  60. Let the machine do the work https://blog.golang.org/generate https://github.com/mmcloughlin/avo Generate x86

    Assembly with Go https://github.com/grpc/grpc-go ( rpc server and client generator ) https://github.com/kubernetes/code-generator Examples include generating Unicode tables in the unicode package, creating efficient methods for encoding and decoding arrays in encoding/gob, producing time zone data in the time package https://github.com/GeertJohan/go.rice ( non-go file build in )
  61. Whatever you do, always check your errors • Go favor

    explicitness, It was a deliberate choice not to incorporate exceptions in Go. • First, there is nothing truly exceptional about errors in computer programs. For instance, the inability to open a file is a common issue that does not deserve special linguistic constructs; if and return are fine. f, err := os.Open(fileName) if err != nil { return err } • Also, if errors use special control structures, error handling distorts the control flow for a program that handles errors.
  62. Flow of control straightforward • The Java-like style of try-catch-finally

    blocks interlaces multiple overlapping flows of control that interact in complex ways. • Although in contrast Go makes it more verbose to check errors, the explicit design keeps the flow of control straightforward—literally. • There is no question the resulting code can be longer, but the clarity and simplicity of such code offsets its verbosity. • Explicit error checking forces the programmer to think about errors—and deal with them—when they arise. Exceptions make it too easy to ignore them rather than handle them, passing the buck up the call stack until it is too late to fix the problem or diagnose it well.
  63. Errors are just values https://blog.golang.org/error-handling-and-go https://godoc.org/golang.org/x/xerrors https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html Values can be

    programmed • Since errors are values, errors can be programmed. • Can be programmed in different ways to suit different situations. • The full power of the Go programming language is available for processing them. https://blog.golang.org/errors-are-values handle errors with grace.
  64. Go & Cloud https://gocloud.dev/ The Go Cloud Development Kit It’s

    like a standard library for the cloud. You can write an application, and just compile it for Mac, compile it for Linux or Windows or different architectures, and it just runs, and it runs without really any extra code, go-cloud’s goal is do the same thing for cloud https://blog.golang.org/go-cloud https://blog.golang.org/gcdk-whats-new-in-march-2019
  65. Reference • https://talks.golang.org/2012/splash.article • https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html • ExpressivenessOfGo-2010 • Another Go

    at Language Design Presentation • https://hub.packtpub.com/why-golan-is-the-fastest-growing-language-on-github/ • https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65 • http://tekjoe.com/2017/06/12/why-gloang-is-great-for-portable-apps/ • https://research.swtch.com/vgo-eng • https://blog.golang.org/ismmkeynote • https://docs.google.com/presentation/d/1JsCKdK_AvDdn8EkummMNvpo7ntqteWQfynq 9hFTCkhQ/edit#slide=id.g39982b998820aa42_64 • https://docs.google.com/presentation/d/1JsCKdK_AvDdn8EkummMNvpo7ntqteWQfynq 9hFTCkhQ/edit#slide=id.g39982b998820aa42_64