The Go Programming Language

Russ Cox


CNS Winter Research Review

January 21, 2010



Go

New

Experimental

Concurrent

Garbage-collected

Systems

Language

Hello, world

package main

import "fmt"

func main() {
	fmt.Printf("Hello, 世界\n")
}

History

Design started in late 2007.

Implementation starting to work mid-2008.

Released as an open source project in November 2009.

Work continues.

Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, Russ Cox, many others

Goals and Motivation

Go fast!

Make programming fun again.

Targeted at systems software, broadly.

Why isn't programming fun?

Compiled, statically-typed languages (C, C++, Java) require too much typing and too much typing:

Dynamic languages (Python, JavaScript) fix these problems (no more types, no more compiler) but introduce others:

Can we combine the best of both?

Why a new language?

No new systems language in 10+ years.

Current languages designed before ...

... rise of large-scale, networked and multicore computing

... rise of Internet-scale distributed development (many libraries)

Go

Make the language fast.

Make the tools fast.

Compilation Demo

Build all standard Go packages: ~120,000 lines of code.

Go in one slide

Lightweight syntax.

Static types: enough to compile well, but inferred much of the time.

Methods: on any type, orthogonal to type system.

Abstract types: interface values, relations inferred statically.

Visibility: inferred from case of name.

First-class functions.

Garbage collection.


Lightweight feel of a scripting language but compiled.

Go, concurrently

Cheap to create a new flow of control (goroutine):

func main() {
	go expensiveComputation(x, y, z)
	anotherExpensiveComputation(a, b, c)
}

Two expensive computations in parallel.

Go, concurrently

Cheap to create a new flow of control (goroutine):

	for {
		rw := l.Accept()
		conn := newConn(rw, handler)
		go conn.serve()
	}

Concurrent web server.

Network connections multiplexed onto epoll.

Go, synchronized

Use explicit messages to communicate and synchronize.

func computeAndSend(ch chan int, x, y, z int) {
	ch <- expensiveComputation(x, y, z)
}

func main() {
	ch := make(chan int)
	go computeAndSend(ch, x, y, z)
	v2 := anotherExpensiveComputation(a, b, c)
	v1 := <-ch
	fmt.Println(v1, v2)
}

Notice communication of result in addition to synchronization.

Go, synchronized

RPC client

func (client *Client) Call(method string, args, reply interface{}) os.Error {
    // Send RPC message.
    call := client.Go(method, args, reply, nil)
	
    // Read reply from Done channel.
    <-call.Done

    return call.Error
}

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Read response from network.

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Look up request by sequence number.

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Decode response fields from payload.

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Tell client that it finished.

Go, synchronized

RPC client demux

func (client *Client) input() {
	for {
		resp := client.readResponse()
		client.mutex.Lock()
		c := client.pending[resp.Seq]
		client.pending[resp.Seq] = c, false
		client.mutex.Unlock()
		if resp.Error != "" {
			c.Error = os.ErrorString(resp.error)
		}
		resp.Decode(c.Reply)
		c.Done <- c
	}
}

Can create multiple Calls with same Done channel and distinguish which finished by inspecting value sent on channel.

Goroutine demo

Chain together 100,000 goroutines connected by 100,001 channels.

Send a value to one end of the chain.

Each passes it along, increments.

Receive value out the other end of the chain.

Go Status

Go Status

Open source:

  • released on November 10, 2009
  • regular releases (~ weekly)
  • all development done in public Mercurial repository
  • outside contributions welcome
  • two independent compiler implementations
  • XML, JSON, HTTP, TLS/SSL, native RPC, (network channels,) ...

Go Status

Open source

Portable:

  • FreeBSD, Linux, OS X (x86, x86-64)
  • (in progress) Linux arm, Native Client x86, Windows x86.

Go Status

Open source

Portable

Still in progress, experimental. Yet to come:

  • production garbage collector
  • generics?
  • exceptions?
  • unions or sum types?

Questions?