blob: dbdf2b83d61bfc1a65eedd4cf9b7b475a7382e0a [file] [log] [blame]
A Taste of Go
August 14, 2014
Robert Griesemer
* 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:
* 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:
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:
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
-- Rob Pike
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!
# 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.
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/
- 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
.code taste/histop.go /mapper START/,/mapper END/
.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
- Powerful tools
# playground, gofmt
- Multi-platform support
- Great documentation