September 22nd, 2014
The Go programming language is an open source programming language from Google that makes it easy to build simple, reliable, and efficient software. It’s part of the programming language lineage that started with Tony Hoare’s Communicating Sequential Processes and includes Occam, Erlang, Newsqueak, and Limbo. In the following 14 slides, we’ll demonstrate some of the differentiating features of the language, including its extremely lightweight concurrency. The project currently has more than 500 contributors, led by Rob Pike, a distinguished engineer at Google, who worked at Bell Labs as a member of the Unix team and co-created Plan 9 and Inferno.
The Go language extends the idea of arrays with slices. A slice points to an array of values and includes a length. T is a slice with elements of type T. In the pictured exercise, we use slices of slices of unsigned bytes to hold the pixels of an image we generate. With package main, programs start running. The import statement is an extended version of C and C++’s include statement; here we are getting the pic file from a Mercurial repository. The := syntax declares and initializes a variable, and the compiler infers a type whenever it can. Also, make is used to create slices and some other types. A for..range loop is the equivalent of C#’s for..in loop.
The Go map statement maps keys to values. As with slice, you create a map with make, not new. In the example above, we are mapping string keys to integer values. Here we demonstrate inserting, updating, deleting, and testing for map elements.
The pictured program prints:
The value: 42
The value: 48 The value: 0
The value: 0
Structs and methods
The Go language lacks classes but has a struct, which is a sequence of named elements, called fields, each with a name and a type. A method is a function with a receiver. A method declaration binds an identifier (the method name) to a method and associates the method with the receiver’s base type. In this example, we declare a Vertex struct to contain two floating point fields, X and Y, and a method Abs. Fields that begin with uppercase letters are public; fields that begin with lowercase letters are private. Fields and methods are addressable through the dot notation; * and & signify pointers, as in C. This program prints 5.
An interface type is defined by a set of methods. A value of interface type can hold any value that implements those methods. In this example, we define an interface Abser and a variable a of type Abser. Note that the assignments in lines 17 and 18 work, but the assignment in line 22 does not even compile. The Abs method of Vertex, which we saw in the previous slide, has a pointer to Vertex type for its receiver, so a *Vertex implements Abser, but a Vertex does not.
The switch statement in Go is similar to the switch statement in other C-like languages, except that the case statements can be types or expressions in addition to simple values, and the cases automatically break unless they end with fallthrough statements. The cases are evaluated in the order they are defined.
Goroutines are, to a rough approximation, extremely lightweight threads, in the spirit of Tony Hoare’s Communicating Sequential Processes. Line 16 in the sample above calls the say function asynchronously, while line 17 calls the say function synchronously. Goroutines, channels, and select statements form the core of Go’s highly scalable concurrency, one of the strongest selling points of the language. The language also has conventional synchronization objects, but they are rarely needed. The program to the left outputs:
Channels in Go provide a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil. In line 16, we create a bidirectional channel of integers. We could also make unidirectional sending <-c and receiving c<- channels. In lines 17 and 18, we call sum asynchronously with slices of the first and second half of a. In line 19, the integer variables x and y receive the two sums from the channel. In line 7, the underscore _, the blank identifier, means to ignore the first result value from the for..range loop, which is the index. The program output is 17 -5 12.
Range and close
A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression. A loop for i := range c receives values from the channel repeatedly until it is closed. The cap of the channel is the capacity, which is the size of the buffer in the channel, set as the optional second argument when you make a channel, as in line 17. Note the compact form of the assignment statements in the fibonacci function. The program output is the first 10 values of the Fibonacci series, 0 through 34.
A select statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a switch statement but with all the cases referring to communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
Here the main function calls the fibonacci function with two unbuffered channels, one for results and one for a quit signal. The fibonacci function uses a select statement to wait on both channels. The anonymous, asynchronous go function that starts at line 21 waits to receive values at line 23, then prints them. After 10 values, it sets the quit channel, so the fibonacci function knows to stop.
Concurrency patterns, example 1
In this example we are using select to create a fan-in goroutine that combines two input channels of string, input1 and input2, into one unbuffered output channel, c. The select statement allows fanIn to listen to both input channels simultaneously and relay whichever is ready to the output channel. It doesn’t matter that both cases are using the same temporary variable name to hold the string from its respective input channel. The example is from Rob Pike’s 2012 talk on Concurrency Patterns in Go.
Concurrency patterns, example 2
This sample implements a parallel search of the Internet, sort of like what Google actually does. To begin with, replicas …Search is a variadic parameter to the function; both Search and Result are types defined elsewhere.
The caller passes N search server functions to the First function, which creates a channel c for results and defines a function to query the ith server and saves it in searchReplica. Then First calls searchReplica asynchronously for all N servers, always returning the answer on channel c, and returns the first result to come back from the N servers. The example is from Rob Pike’s 2012 talk on Concurrency Patterns in Go.
The Go net/http package provides HTTP client and server implementations. This example implements a simple Web server that returns the contents of the directory /usr/share/doc to a Web client. The example does not work properly in the Go Playground online environment, but run on a Mac command line, it returns the following to a Web browser asking for http://localhost:8080/: