When I first started learning Go, I was excited about the ideas of what the language is supposed to be. Built-in concurrency primitives that aren’t just a wrapper around threads! A statically typed, compiled language without the C preprocessor or page-long compiler errors from C++ templates! And it compiles to bare metal, so we don’t have to deal with a JVM! Even an established style guide with built-in formatters to avoid the inevitable arguments about arbitrary spacing choices!
Despite all that, I always got a vaguely negative feeling about writing actual code. At first, I just chalked it up to inexperience in the language and moved on with my job. Now that I’ve been using Go long enough to complete a few real programs and get up to a few thousand lines, I think I have enough experience to start giving some credence to my opinions. Go has several great features, but overall I have to describe the language as a study in false economy.
Go is a statically typed, compiled language, but Go’s designers wanted to approach the rapid development cycles of scripting languages by shrinking the compile time as much as possible. Of course, this comes with tradeoffs; every feature the compiler needs to implement adds time, and for some features, the best known algorithms aren’t fast. In order to keep the compiler fast, at nearly every place where the designers had to make a tradeoff between compilation speed and expressiveness, they chose speed.
There’s only so fast you can get by speeding up the compiler. To shrink that development cycle even more, Go has a number of “shorthand” features that let you save a few characters here and there by doing things like dropping type annotations in certain circumstances, changing whether a symbol is exported by changing the case of its name, etc. They add even more “quickness” by advocating a naming style that uses very short (often single letter) variable names. All of these choices prioritize the convenience and speed of the writer over the reader and subsequent maintainers.
To see why this matters, it’s important to think about what kind of tasks we have in programming. There are things like describing a problem, designing an algorithm, mapping the algorithm to code (writing code), reading somebody else’s code, updating already written code later, etc. The first two tasks don’t have much to do with the language you’re using, but writing, reading, and changing code directly depend on what kind of syntax and features your chosen language offers.
This is where the language can help or hinder. To turn an algorithm into a real program, I have to keep the state of my program in my mind. I have to remember what my various variables or functions are for, what they represent or compute, how they can be used, what assumptions or limitations come with them, etc. A language that reduces this mental state makes it easier to write a correct program. It also makes it easier to understand a program later so that I can modify it. A language that increases this mental state makes both of these harder. The tradeoffs in Go that I described above all have the effect of letting the code writer go faster by increasing the mental burden in the reader.
I originally planned to include a few examples here, but this entry started getting too long. Instead, in the next couple of blog entries, we’ll take a look at a few examples.