Time To Go?

As a longtime Java developer, I had missed the introduction of Google’s Go language. With some recent changes in my work priorities, I am now looking at it, and thought I would share my first impressions.

Go Features I Like

If I had to pick one feature of Go that I really like and wish Java would adopt, it would be multivariable assignment.

There are a number of common use cases in which the program needs to obtain multiple pieces of information from a single call. In Java, you do these by creating a single object to hold these. For example, to iterate through a map, you could write something like this:

int highestScore = -1;
String winner = null;
for (Map.Entry<String,Integer> entry : scores.entrySet()) {
    if (entry.key() > highestScore) {
        winner = entry.getKey();
        highestScore = entry.getValue();
    }
}

or if you wanted multiple values from a method,

Pair<String,Integer> highest = getHighestScore();
System.out.printf("%s is the winner, with a score of %d!%n", 
                  highest.first, highest.second);

In both cases, the program needs to allocate an object on the heap to hold the values, and then garbage collect it afterwards. If you do this a lot, it could well make a difference in performance, but in any case, it certainly adds to the code that needs to be written. Go, on the other hand, takes a feature of Python and handles this more simply:

highestScore := -1
var winner string
for contestant,score := range scores {
    if score > highestScore {
        winner = contestant
        highestScore = score
    }
}

or

name,score := GetHighestScore()
fmt.Printf("%s is the winner, with a score of %d!\n", name, score)

both of which are simpler and avoid the extra allocation.

But this is hardly the only place where I am feeling a bit of feature envy. An occasional source for errors in Java is the confusion over variable initialization. Fields are initialized by default to a sensible zero value, whether literally zero for numbers, or null for allocated values. Local variables in methods, on the other hand, are not – and this can sometimes lead to incorrect programs when the programmer forgets to initialize a local variable. Good IDEs may warn of such cases, but cannot always be accurate in their paths.

I will also note that the Go designers must have tired of the constant wars over whether opening braces belong on the same line as a block statement, or go on the next line in order to align with the closing brace. In Go, the following results in a compiler error:

if score > highestScore 
{
    winner = contestant
    highestScore = score
}

which stops those arguments cold.

Go Features I Don’t Like

If you are a Java coder, you might have been thrown a bit by multiple ways of defining and setting variables in the above code. The most obvious confusion would be the following:

highestScore := -1
    :
    :
highestScore = score

The first of these lines defines the variable highestScore, inferring that it is an int. The second assigns a new value to that same variable. Think about that for a minute. You have to use one operator to define a variable and another to give it a new value. Of course, there is another way to do this:

var highestScore = -1

would do the exact same thing as that first line, above. And if you want just to use the default value, you can do so without assignment, but that means that you need to be explicit as to the type:

var highestScore int

Which makes four different ways to define/set variables, as opposed to just one in Java, which is a bit disconcerting.

Of course, that’s just part of the learning curve. There is one feature of the language that I absolutely hate: no support for exceptions. To be sure, there is something called panic, which is kind of like an exception, but that is used for really extraordinary conditions, on the order of null pointer exceptions and array index out-of-bounds, which generally indicate something that is likely unrecoverable. But normal I/O errors, such as file-not-found return error codes.

Now for a few decades, the rule has generally been to use exceptions to handle exceptional conditions. Error codes, we’ve been told, should be avoided for a number of reasons, including the likelihood that they will be ignored. Exceptions are not as easily ignored, as you have no choice but to handle them somehow, or have the program exit.

Of course, another reason to avoid them has been the reality that in most languages, you have the choice of returning a real result or an error code, but not both. Apparently, the ability to return multiple results led the language designers to return an error code in addition to a result, meaning that the caller needs to check it:

f, err := os.Open(filename)
if err != nil {
    return err
}

which is really ugly.

But what really concerns me is the implications for writing clean code in Go. In a future post, I will go into some problems I see and my initial thoughts on how to resolve them.