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:
- verbose, lots of repetition
- too much focus on type hierarchy
- types get in the way as much as they help
- compiles take far too long
Dynamic languages (Python, JavaScript) fix these problems (no more types, no more compiler) but introduce others:
- errors at run time that should be caught statically
- no compilation means slow code
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.
- many blocked Read calls != many blocked OS threads
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?