Google's Go Revisited

At the end of 2009, a delightfully “little” programming language was fully released following a few years of gestation. I wrote back then about the major features of Google’s Go.

Nearly two years of intensive Scala, Java and Groovy work later, I felt it was time to revisit Go with one question in mind: with so much new-found enthusiasm for non-Java languages around, what has Go got to offer that might make it stand out from the crowd? Or, put more simply, does it really have anything new to say as a programming language?

Is Go Object Oriented?

Yes - but not like Java/C++. Go has classes, but no ‘class’ keyword introduces the relationship between Go’s struct data records and its functions. Classes exist because functions are attached to structs (via a so-called receiver parameter).

Go has interfaces too, but no need for explicitly specifying which classes implement them. A different and more minimalist approach has been taken, yet it seems this novel approach is just as valid and capable.

There’s no inheritance. This sounds radical but they found a way that you don’t need it. And you don’t. The polymorphism achieved using interfaces is sufficient and can actually promote better code modularity. The absence of a class hierarchy makes refactoring easier and so Go sits well with an agile development mindset.

Is Go Statically-Typed?

Yes - but not like Scala or Java. There is no need of a class hierarchy. The keyword type introduces a new type (like in Ada), not just an alias (like in C). And type declarations are important, unlike those in Scala that only exist within containing classes. But like Scala, use of types is uncluttered, not demanding the verbose ceremony needed in Java and C++.

Because Go compiles so fast (maybe orders of magnitude faster than Java and Scala), it can feel more like a dynamically-typed language, even though it provides thorough compile-time checking.

Is Go Functional?

Yes - but not like Haskell/Scala. Functions in Go are first-class item and can be used as closures. Immutability is possible using const declarations. However, the emphasis is quite different. For example, there’s no particular encouragement to avoid having mutable temporary variables by using recursion.

Also, Go functions return any number of arguments, a feature found in Scala and many much older languages (e.g. Ada, Occam) but sorely missing from Java.

Is Go A Parallel Processing Concurrent Language?

Yes, unlike Java or Scala, Go is a language properly supporting concurrency and therefore parallelism. But Rob Pike emphasises concurrency rather than parallelism: “It’s not about parallelism. Concurrent programming allows parallelism but that’s not what it’s really for. It’s about expressing program structure to represent independently executing actions. … Concurrency is about program design” (Google I/O 2010 - Go Programming)

Go has three means for expressing concurrency:

  • goroutines are (very) light-weight threads that get scheduled a slice of CPU time by the scheduler in Go’s runtime. If you’re thinking of Java threads or Posix threads, you’re thinking of something else. Goroutines have little overhead, so using them - lots of them - is cheap.

  • channels provide communication between goroutines. Based on Hoare’s CSP, they are a simple synchronisation feature by which one goroutine passes something to another. The exchange can be buffered (asychronous) but works just as well without buffering. The exchanged content can be data, a closure or even the end of another channel. Passing channel ends down channels was conceived in Milner’s Pi calculus and is also implemented in Occam-Pi.

  • selection allows goroutines to respond to events from more than one source. Choosing between alternatives is a core part of CSP (which uses the term nondeterministic choice for this) and is an important feature in allowing a given goroutine to control its own destiny. An OO object has no choice about which methods it endures. An actor (Scala, Akka etc) receives messages from only one input queue. But if a goroutine has multiple channels, it can select the next event from those channels, and if necessary ignoring some it doesn’t care about in its current state.

Unlike mainstream APIs for multithreading, Go does not need mutexes for protecting shared memory. Furthermore, blocking synchronisation is the norm. Yet because everything is highly concurrent, throughput is rapid and efficient. This illustrates that, in such circumstances, blocking is not evil. Indeed quite the opposite, it helps the programmer by allowing simple reasoning about events between elements.

Summary

Go is definitely worth deeper investigation. It looks a bit like C syntactically but is radically different. It’s simple but embodies many things. Because Go’s concurrency epitomises the radical thinking, the final word goes to Rob Pike : Concurrency is not just parallelism, but it’s a great way to structure software.

 
comments powered by Disqus