Earlier this year, we ran an article about the emerging crop of native languages. The group included D, Go, Rust, and Vala. I promised then that we would eventually explore these languages in greater detail. This week starts that fun journey with what will be a five-part series on Go, the new language from Google. Unlike past tutorial series in Dr. Dobb's, we will run the articles in consecutive weeks, so that you can be up and running quickly.
More than the other languages on the list, Go really attracts me. While I am by no means an expert in it, I like what I've seen so far. My enjoyment derives, as you'll see, from an appreciation of the maturity of feature selection, rather than from a gee-whiz enthusiasm that new languages can generate on first exposure. (I confess I am susceptible to this kind of excitement as well—which is why I can recognize the difference with Go.) These features in particular appeal to me:
Fast and simple compilation. Go compiles fast. In fact, it compiles so fast, it can easily be used as a scripting language. Several reasons for the compilation speed are that it does not use header files; if a module depends on A, which in turn depends on B, a change in A requires only recompiling the original module and the A dependency; finally, the object modules contain enough dependency information that the compiler needs no make file. You simply compile the main module and it will automatically compile everything in the project that needs to be updated. Cool, eh?
Error handling through multiple return values. There are two primary paradigms today in native languages for handling errors: return codes, as in C, or exceptions, as in OO alternatives. Neither system is ideal. But of the two, return codes is the more frustrating, because returning error codes often conflicts with returning other data from the function. Go solves this by allowing functions to return multiple values. One value that returned for functions that you designate is of type error and can be checked anytime a function returns. If you don't care about the error value, you don't check it. In either case, the regular return values of the function are available to you.
Simplified composition (in preference to inheritance). Types can be qualified as members of a group of objects by the use of interfaces, which, like they do in Java, specify behavior. So, for example, the io package in the standard library defines a Writer that specifies one method, a Write function with array of chars as the the input parameter and return values of integer and error type). Any type that implements a Write method with the same signature is a de facto implementation of the io.Writer interface. This design decouples code rather elegantly. It also simplifies mocking objects for unit testing. For example, if you want to test one method in a Database object, in standard languages you have to create a database object that requires lots of initialization and protocol in order to create the mock. In Go, if the method under test implements an interface, you can create any object with that interface and you're good to go. So, you could create MockDatabase, which is a minimal object that implements only the few methods necessary to run the interface that needs to be mocked—no constructor, no added features, just the methods.
Simplified concurrency. Concurrency in Go is made comparatively easy. Put the keyword 'go' in front of any function and that function will run in its own go-routine (a very light thread). Go-routines communicate through channels that are essentially blocking message queues. The usual tools for mutual exclusion are available, but Go makes it simple just to kick off concurrent tasks and coordinate via channels.
Excellent error messages. No language I've seen comes close to Go in the quality of the diagnostics it emits. For example, if a program deadlocks, the Go runtime notifies you of this and, to the extent it can, tells you which threads are causing the deadlock! Compiler error messages are similarly detailed and useful.
Grab bag: There are other appealing features, which I'll quickly run through here: higher-order functions, garbage collection, hash maps and expandable arrays built into the language (so part of the language syntax, not brought in as a library), and so on.
Of course, not everything is rainbows and lollipops. The tools are still fairly immature and the community is small, but with Google behind the language, both aspects are sure to be remediated. While many languages—notably D, Rust, and Vala—aim to simplify C++ and add specific features, they all feel to me like “C++ with better features.” In Go, however, there's been a fundamental rethinking of how native languages should operate. And from that perspective comes an elegance of implementation that does away with many problems. Even if you have no particular need to consider Go, I think that by kicking the tires on the language, you'll find yourself appreciating many of its features. Cheers!