An overview of Go

This article is based on the first half of a presentation I gave at PyMNtos in December, 2011.

Let’s start at the beginning: What is Go?

Go is a “fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.” It is a systems language competitive with C, C++, and Java, but designed to deal gracefully with concurrency. To wit:

“Go makes much more sense for the class of problems that C++ was originally intended to solve.” —Bruce Eckel

Go was released by Google in late 2009, and Version 1.0 should land in the first few months of 2012.

Why should you care?

You should care about Go for five reasons:

  1. It has an impeccable pedigree.
  2. It embodies an extremely pragmatic design philosophy.
  3. It implements many innovative ideas.
  4. It can already be used for real work.
  5. It has an absolutely fantastic mascot.

Go’s Pedigree

Go is being developed by Bell Labs veterans augmented by a handful of other brilliant engineers. The core team includes:

The team’s experience is humbling. If nothing else, subscribe to the Go blog. You will learn something.

Go’s Pragmatic Design Philosophy

Go is an extremely pragmatic language. One of its central design tenets is the idea that interpreted languages (Perl, Python, Ruby, etc.) are popular not due to any inherent property of being interpreted, but rather because they often have better syntactic constructs, are more expressive than their compiled counterparts, and don’t force developers to sit through long compile times.

By taking these lessons into account, Go hopes to offer the developer-time efficiency of a language like Python and the run-time efficiency of C.

For example:

Let’s look at a few of these more closely.

Quick Compile Times

Go programs are structured in a manner that drastically limits the number of passes required to produce a binary. My 1.6 GHz dual-core laptop compiles pkg/math (72 files, 5 KLOC) in under 1 second:

$ time make --quiet
make --quiet  0.89s user 0.08s system 97% cpu 0.991 total

Clean Syntax

Below is a simple “hello, world” in Go.

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

The anatomy of this program should be self-evident: It declares that it is a member of the package main, imports any other packages it needs, and then defines a function called main, which is executed when the compiled program is invoked.

It’s worth pointing out the use of Unicode. By definition, Go source files are encoded in UTF-8, which makes it easy to use Unicode literals. Have you ever wanted to name a variable Δ? Well, now you can.

First Class Functions

In Go, functions can be created or passed around like any other structure. In the code below, I’ve defined two unary functions: double and square. Each accepts a single int for input and returns a single int.

I’ve also defined apply, which accepts a two arguments: a function, and an integer. It then calls the function and returns an integer.

package main

import "fmt"

func double(x int) int {
    return x + x
}

func square(x int) int {
    return x * x
}

func apply(f func(int) int, x int) int {
    return f(x)
}

func main() {
    fmt.Println(apply(double, 5))
    fmt.Println(apply(square, 5))
}

Because Go is strongly and statically typed, function declarations must explicitly name the types of their inputs and return values. Thus, func apply(f func(int) int, x int) int creates a new function named apply which takes two inputs, a func(int) int called f and an int called x. It then returns an int.

You can read func(int) int the same way: It’s a function that accepts an integer and returns an integer.

With that information, the compiler is able to verify that everything in the program is of the expected type. It’s worth noting that Go does not do implicit or automatic type coercion.

Closures

A closure is just a function that can reference variables that would typically be considered outside of its scope. To illustrate, the code below calculates the first few Fibonacci numbers using a closure.

package main

func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return b
    }
}

func main() {
    f := fib()
    println(f(), f(), f(), f(), f())
}

That might look a bit weird. To review, note that named function definitions have three parts:

  1. The func keyword.
  2. The name of the function, its inputs, and their types, like square(x int).
  3. The function’s return type, like int.

So func fib() func() int above is creating a function named fib that doesn’t have any inputs, and which returns a func() int. A func() int is, in turn, a function that doesn’t have any inputs, and which returns an integer.

Thus, when we invoke fib() we get another function. When we invoke that, we get an integer.

Because functions in Go are closures, the function returned by fib() has access to the values of a and b, even though they were defined outside of the inner function itself.

Go’s Innovations

The following items aren’t necessarily unique to Go, but they are distinguishing features.

The type system and concurrency features are worthy of their own dedicated articles, so let’s look at the other items.

Gofmt, the Go Pretty-Printer

Go is somewhere between C and Python in the rigidity of its syntax. Whitespace is insignificant and semicolons can be omitted, but the opening brace of a construct must be on the same line as that construct.

Still, Go maintains high degree of readability across authors thanks to gofmt, a command line tool that pretty-prints Go source code in a single, consistent style.

Take the following version of the Fibonacci program from above. It still compiles and runs perfectly, but it has semicolons everywhere, horribly inconsistent indentation, and even combines two statements on one line with a, b = b, a+b; return b;

package main

func fib() func() int {
        a, b := 0, 1;
    return func() int { a, b = b, a+b; return b;
                      }
        }

func main() { f := fib();
println(f(), f(), f(), f(), f());
}

One pass through gofmt, and out pops:

package main

func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return b
    }
}

func main() {
    f := fib()
    println(f(), f(), f(), f(), f())
}

Gofix, the Old-to-New Go Compiler

As Go is still under rapid development, it’s not uncommon for components of the standard library to change in backwards-incompatible ways. Rather than paper over these changes with shims, the Go team ships gofix, a compiler that reads in Go code written against old APIs, and updates it to use newer ones.

For example, earlier this year, the code below would compile, parse a URL, and print out the value of the parameter q:

package main

import "http"

func main() {
    url, _ := http.ParseURL("http://example.com/search?q=foo")
    println(url.Query().Get("q"))
}

However, trying to compile that code today fails:

$ 6g old.go
old.go:6: undefined: http.ParseURL

This fails because the version of Go released in September 2011 took all of the url-related functions out of the http package and put them in their own url package.

Running the code through gofix updates it to use the renamed functions:

package main

import "url"

func main() {
    url_, _ := url.Parse("http://example.com/search?q=foo")
    println(url_.Query().Get("q"))
}

Note that import "http" changed into import "url" and http.ParseURL was changed into url.Parse. One of the more subtle details is that my variable, url, was automatically renamed to url_ to avoid clashing with the new import statement. How cool is that?

If you’re coming from Python, you can think of gofix as being similar to 2to3.

Deferred Execution

Take the following program:

package main

func goodbye() {
    println("Goodbye!")
}

func main() {
    goodbye()
    println("Four")
    println("Three")
    println("One")
    println("Two")
}

When run, it prints out five lines in their order of appearance in the file:

$ ./defer1
Goodbye!
Four
Three
One
Two

But what if goodbye() handled some sort of cleanup, like closing a file, and we wanted it to run at the end of the function. We could move it to the end, but that might mean separating it from the thing that it’s cleaning up after.

Python has Context Managers (with blocks) to handle this sort of scenario. Go has the defer statement. By prefixing any function call with defer, that call gets queued and only runs right before the enclosing function completes.

Let’s look at what happens if we defer the call to goodbye():

func main() {
    defer goodbye()
    println("Four")
    println("Three")
    println("One")
    println("Two")
}

Now running the program prints “goodbye!” at the end:

$ ./defer2
Four
Three
One
Two
Goodbye!

And all it took was adding a simple defer keyword in front of the call.

It’s also worth noting that you can defer multiple calls within a function, and when that function completes, they’re evaluated in a last in, first out (LIFO) order.

Thus, by deferring the first two calls to println()

func main() {
    defer goodbye()
    defer println("Four")
    defer println("Three")
    println("One")
    println("Two")
}

…we can get the “correct” ordering in the output:

$ ./defer3
One
Two
Three
Four
Goodbye!

While most immediately useful for closing files or tracing execution, deferred functions can also be used to inspect and modify named return values. This is possible because deferred functions execute after their parent has completed, but before it actually returns values to its caller.

Using Go for Real Work

Go currently runs well on 32 and 64 bit Linux, FreeBSD, and Mac OS X. Ports to Linux/ARM and Windows/x86 are in the works.

At the time of writing, Go is only two years old. So who’s using it? Google, for one.

“We’re already using Go internally at Google for some production stuff” —Rob Pike, May 2010

Heroku has also been using Go to build Doozer, a consistent, highly-available data store. To quote Keith Rarick, “This is the first significant thing we’ve done in Go, but it won’t be the last. Go has been an absolute joy to use.”

It’s also worth noting that at the time of writing two of the top three competitors in The AI Challenge are written in Go.

Lastly, Google App Engine has experimental support for deploying projects written in Go.

Go’s Fantastic Mascot

If you’re familiar with Plan 9, you’re already familiar with Glenda, the Plan 9 Bunny.

Glenda, the Plan 9 Bunny

In that same vein, I present the Go Gopher:

The Go Gopher

The Go Gopher has also made appearances as collectable toys…

Go Gopher Toys

…and as a pair of rather special Halloween costumes:

Go Gopher Costumes

Isn’t it wonderful?

To learn more about Go, check out:

Credits

Images in the post are courtesy of the Go Blog, the Go source code repository, and Plan 9 from Bell Labs. The Gopher costume photograph is attributed to Chris Nokleberg. Hello World and Fibonacci examples from golang.org.