| A Taste of Go |
| August 14, 2014 |
| |
| Robert Griesemer |
| Google |
| gri@golang.org |
| |
| |
| * The Go programming language |
| |
| - Modern |
| # OO support but not type-oriented |
| # strong support for concurrency |
| - Compact, concise, general-purpose |
| - Imperative, statically type-checked, dynamically type-safe |
| - Garbage-collected |
| - Compiles to native code, statically linked |
| - Fast compilation, efficient execution |
| |
| Designed by programmers for programmers! |
| |
| |
| * Hello, World! |
| |
| .play taste/hello.go |
| |
| # Unicode |
| # Programs are organized in packages |
| # A package is a set of package files |
| # A package file expresses its dependencies on other packages via import declarations |
| # The remainder of a package file is a list of (constant, variable, type, and function) declarations |
| |
| |
| * Hello, World! Internet-style |
| |
| .play taste/hellohttp.go |
| |
| |
| * Program elements |
| |
| * Constants |
| |
| - Maintained precisely: |
| const e = 2.71828182845904523536028747135266249775724709369995957496696763 |
| const third = 1.0/3 |
| - Typed or without type: |
| const M64 int64 = 1<<20 |
| const M = 1<<20 |
| - Evaluated at compile-time: |
| const big = 1<<100 / 1e30 // valid constant expression |
| |
| Compiler complains if a constant doesn't fit where it is _used_. |
| |
| |
| * Variables |
| |
| - Statically typed: |
| var x int |
| var s, t string |
| - Implicitly or explicitly initialized: |
| var x int |
| var s, t string = "foo", "bar" // multiple assignment |
| |
| var x = 42 // int |
| var s, b = "foo", true // string, bool |
| - Short variable declaration (inside functions only): |
| x := 42 |
| s, b := "foo", true |
| - Can safely take address of _any_ variable! |
| return &x |
| # compiler will do the right thing |
| |
| |
| * Types |
| |
| - Predeclared types, the usual suspects: |
| uint8 (byte), uint16, uint32, uint32, uint64, |
| int8, int16, int32, int32 (rune), int64, |
| float32, float64, |
| complex64, complex128, |
| uint, int, uintptr, |
| bool, string, |
| error // not so usual |
| |
| - Composite types: |
| array, struct, pointer, function, |
| slice, map, channel |
| |
| - Abstract type: |
| interface |
| |
| |
| * Type declarations |
| |
| - Composition from left-to-right (Pascal style): |
| [10]byte // array of 10 bytes |
| |
| struct { |
| name string |
| left, right *Node |
| action func(*Node) |
| } |
| |
| func(a, b, c int) |
| func(http.ResponseWriter, *http.Request) error |
| |
| - A type declaration defines a _new_ type: |
| type Weekday int |
| |
| type Point struct { |
| x, y int |
| } |
| |
| |
| * Slices |
| |
| []T // slice of T |
| |
| - Descriptor for an underlying array segment |
| - May grow and shrink |
| - Has length and capacity |
| - Assigning a slice copies the descriptor, not the underlying array |
| |
| Common slice operations: |
| |
| len(s) |
| s[i] |
| s[i:j] |
| append(s, x) // append element x to slice s and return new slice |
| |
| - Slices play the role of dynamically sized arrays |
| - Widely used in Go code |
| |
| |
| * Maps |
| |
| map[K]V // map K -> V |
| |
| - Map is a language-supplied hash table |
| - Maps values of key type K to values of type V |
| - Assigning a map copies the map reference, not the map contents |
| |
| Common map operations: |
| |
| make(map[K]V) |
| len(m) |
| m[k] |
| delete(m, k) |
| |
| - Map iteration order is not specified: |
| |
| for key, value := range m { |
| // order of key sequence different each time |
| } |
| |
| * Statements |
| |
| - Curly braces (C style) |
| - Multiple assignments and some other new constructs |
| - Many cleanups: mandatory braces, no parentheses for conditionals, implicit break in switches, no semicolons, etc. |
| a, b = b, a // swap |
| f, err = os.Open(filename) |
| |
| if x < y { |
| return x |
| } else { |
| return y |
| } |
| |
| switch day { |
| case Mon: |
| ... |
| // break is implicit |
| case Tue, Wed: |
| ... |
| } |
| |
| |
| * A few words on syntax |
| |
| _Syntax_doesn't_matter_unless_you_are_a_programmer._ |
| -- Rob Pike |
| |
| Corollary: |
| |
| _Compactness_of_syntax_doesn't_matter_unless_you_are_reading_programs._ |
| |
| Compact is not the same as terse. Readability is crucial. |
| |
| |
| * An example: IndexOfAny in Java |
| |
| public static int IndexOfAny(String str, char[] chars) { |
| if (isEmpty(str) || ArrayUtils.isEmpty(chars)) { |
| return -1; |
| } |
| for (int i = 0; i < str.length(); i++) { |
| char ch = str.charAt(i); |
| for (int j = 0; j < chars.length; j++) { |
| if (chars[j] == ch) { |
| return i; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| 299 chars (100%), 101 tokens (100%) |
| |
| |
| * IndexOfAny in Go |
| |
| .code taste/examples.go /IndexOfAny START/,/IndexOfAny END/ |
| |
| 217 chars (73%), 62 tokens (61%) |
| |
| Almost 30% less text and a surprising 40% fewer tokens to read! |
| |
| _Less_clutter_means_reduced_cognitive_load._ |
| |
| # minimum improvement |
| # typical Go programs tend to require much less than 70% of the code size of equivalent programs in other languages |
| |
| |
| * Functions |
| |
| - Regular functions |
| func Sin(x float64) float64 |
| func AddScale(x, y int, f float64) int |
| |
| - Multiple return values |
| func Write(data []byte) (written int, err error) |
| |
| - Variadic parameter lists without magic |
| func Printf(format string, args ...interface{}) |
| |
| - Functions are first-class values |
| var delta int |
| return func(x int) int { return x + delta } |
| |
| |
| * Function values: An example |
| |
| // walkStdLib calls f with the filename of each .go |
| // file in the std library until f return false. |
| func walkStdLib(f func(filename string) bool) |
| |
| Calling walkStdLib with a closure: |
| |
| .code taste/walk.go /example START/,/example END/ |
| |
| More directly: |
| |
| .play taste/walk.go /main START/,/main END/ |
| |
| |
| * Methods |
| |
| Methods are functions with a _receiver_ parameter: |
| |
| .code taste/point.go /String START/,/String END/ |
| |
| The receiver binds the method to its _base_type_ (Point): |
| |
| .code taste/point.go /Point START/,/Point END/ |
| |
| Methods are invoked via the usual dot notation: |
| |
| .play taste/point.go /main START/,/main END/ |
| |
| |
| * Methods can be defined for any user-defined type! |
| |
| For the Weekday type: |
| |
| .code taste/weekday.go /type START/,/type END/ |
| |
| Define String method on Weekday: |
| |
| .code taste/weekday.go /String START/,/String END/ |
| |
| .play taste/weekday.go /main START/,/main END/ |
| |
| Method calls via non-interface types are statically dispatched. |
| |
| * Interface types |
| |
| - Abstract |
| - Define (possibly empty) set of method signatures |
| - Values of _any_type_ that implement all methods of an interface can be assigned to a variable of that interface. |
| |
| Examples: |
| |
| interface{} // empty interface |
| |
| interface { |
| String() string |
| } |
| |
| interface { |
| Len() int |
| Swap(i, j int) |
| Less(i, j int) bool |
| } |
| |
| |
| * Using interfaces |
| |
| .code taste/stringer.go /Stringer START/,/Stringer END/ |
| |
| Both Weekday and Point define a String method, so values of both can be assigned to |
| a variable of Stringer type: |
| |
| .play taste/stringer.go /main START/,/main END/ |
| |
| Method calls via interface types are dynamically dispatched ("virtual function call"). |
| |
| |
| * A larger example |
| |
| |
| * Top 10 identifiers in std library |
| |
| .code taste/idents.go |
| |
| $ cat $(find $GOROOT -name '*.go') | ./idents | sort | uniq -c | sort -nr | sed 10q |
| |
| |
| * A variation: Histogram of Go statements |
| |
| A histogram is a map from statement name ("if", "for", etc.) to use count: |
| |
| .code taste/histo0.go /histogram START/,/histogram END/ |
| |
| Algorithm: |
| |
| - Use walkStdLib to traverse all files of the std library |
| - For each file, parse and create abstract syntax tree |
| - Traverse syntax tree and add each statement to histogram |
| - Print the result |
| |
| .code taste/histo0.go /main START/,/main END/ |
| |
| |
| * Processing a Go source file |
| |
| .code taste/histo0.go /add START/,/add END/ |
| |
| |
| * Printing the histogram |
| |
| .play taste/histo0.go /print START/,/print END/ |
| |
| Note: Histogram (map) iteration order is not specified. |
| |
| * Sorting |
| |
| sort.Sort operates on any type that implements the sort.Interface: |
| |
| interface { |
| Len() int |
| Swap(i, j int) |
| Less(i, j int) bool |
| } |
| |
| For instance, to sort a slice of strings lexically, define: |
| |
| .code taste/sort.go /lexical START/,/lexical END/ |
| |
| And sort: |
| |
| sort.Sort(lexical(s)) // where s is a []string slice |
| |
| |
| * Sorting histogram entries |
| |
| .code taste/histo.go /byCount START/,/byCount END/ |
| |
| |
| * Improved histogram printing |
| |
| .play taste/histo.go /print START/,/print END/ |
| |
| |
| * Concurrency |
| |
| * Goroutines |
| |
| - The _go_ statement launches a function call as a goroutine |
| go f() |
| go f(x, y, ...) |
| |
| - A goroutine runs concurrently (but not necessarily in parallel) |
| - A goroutine has its own stack |
| |
| |
| * A simple example |
| |
| .code taste/concurrency1.go /f START/,/f END/ |
| |
| Function f is launched as 3 different goroutines, all running concurrently: |
| |
| .play taste/concurrency1.go /main START/,/main END/ |
| |
| |
| * Communication via channels |
| |
| A channel type specifies a channel value type (and possibly a communication direction): |
| |
| chan int |
| chan<- string // send-only channel |
| <-chan T // receive-only channel |
| |
| A channel is a variable of channel type: |
| |
| var ch chan int |
| ch := make(chan int) // declare and initialize with newly made channel |
| |
| A channel permits _sending_ and _receiving_ values: |
| |
| ch <- 1 // send value 1 on channel ch |
| x = <-ch // receive a value from channel ch (and assign to x) |
| |
| Channel operations synchronize the communicating goroutines. |
| |
| * Communicating goroutines |
| |
| Each goroutine sends its results via channel ch: |
| |
| .code taste/concurrency2.go /f START/,/f END/ |
| |
| The main goroutine receives (and prints) all results from the same channel: |
| |
| .play taste/concurrency2.go /main START/,/main END/ |
| |
| |
| * Putting it all together |
| |
| |
| * Analyze files concurrently, map-reduce style |
| |
| Mapper: |
| |
| .code taste/histop.go /mapper START/,/mapper END/ |
| |
| Reducer: |
| |
| .code taste/histop.go /reducer START/,/reducer END/ |
| |
| .code taste/histop.go /merge START/,/merge END/ |
| |
| |
| * From sequential program... |
| |
| .play taste/histo.go /main START/,/main END/ |
| |
| |
| * ... to concurrent program |
| |
| .play taste/histop.go /main START/,/main END/ |
| |
| |
| * There's a lot more! |
| |
| - Extensive standard library |
| |
| .link http://golang.org/pkg/ |
| |
| - Powerful tools |
| |
| .link http://play.golang.org/p/Au02fFpYdf |
| # playground, gofmt |
| |
| - Multi-platform support |
| |
| .link http://build.golang.org/ |
| |
| - Great documentation |
| |
| .link http://tour.golang.org/#1 |