Things I like about Go

June 22, 2014

Writing a reasonable sized Go program has wound up giving me a bigger exposure to Go, reinforcing some of my existing views on it and giving me others. Today I want to talk about some nice things that I think Go does, things that make it easier to write code in it than I initially thought it might be. This is an incomplete list and I'm sure I'll find or think of other things in the future.

  • := type inference means that much of my code is free of any particular mention of actual types. Among other things this makes it much less of a pain to change the type of something since far fewer spots will need updating.

    The one nit I have is that I really wish there was some form of return value type inference. As it stands, every Go function needs to explicitly declare its return type, which can cause a bunch of heartburn if you change a type that gets returned up through a call stack.

  • Coming from Python, it's great that strings are generally a really lightweight thing that you can take subsets of any time you want to. My Go code makes new strings all the time and in Python all of them would involve significant memory copying. Byte arrays and array slices in general have similar properties.

    Strings have a bad side but the bad side is subtle and doesn't really apply until you're caring about memory usage (the same applies for slices of byte arrays).

    (If you look at Go from a C perspective instead of a Python one, strings are a massive productivity accelerator. Even if you already have a good buffered-strings library you like, simply dealing with it involves a lot of hassle that Go's built in good strings save you from.)

  • That the . operator can be applied equally to pointers and values, instead of needing C's . versus -> depending on whether you have an actual struct or a pointer to it. This especially helps with embedded structs and struct pointers, which Go encourages you to use. It also makes pointer-receiving method functions just a bit nicer.

    (. obscures this so much that I recently forgot that my code had a pointer instead of an actual struct and took the address of a pointer. Go's static typing promptly told me that I was trying to assign a **Rule to something that took a *Rule.)

  • The testing package, especially advanced aspects of it like test coverage. Again this is a cool thing from a C perspective more than a Python one, although Go goes out of its way to make it easy to test things.

  • gofmt, which doesn't just create a standard style but also saves me work. I can write a sloppily formatted list of constants or whatever and then just gofmt the file (well, the buffer, in GNU Emacs) and magically everything has been spaced out, lined up right, and so on with no effort on my part.

    (I could pick nits with some of gofmt's choices but it's not worth it. There's no point fighting Go's city hall here, which is of course sort of the point of gofmt.)

  • The fmt %v, %+v, and %#v verbs, which provide basic data structure introspection for debugging purposes. Even from a Python perspective this is nice; in Python you have to go out of your way to provide a useful debugging representation of a struct equivalent while Go just does it for you.

  • That the Go people are perfectly prepared to resort to brute force instead of (excessive) cleverness and thus that when you read the code in, eg, the standard packages you get inspired to do the same.

    As an example, I'll take how the standard Go templating system for both text/template and html/template does line numbers for errors. Rather than carefully tracking line numbers as it parses its way through a block of text, it simply exposes the absolute position in the block text. If it needs to know the line number of an absolute position, it just counts how many \n's there are between the start of the text and the position. This is a marvelously simple brute force approach to the problem and sure, it's inefficient, but generally you don't care about that in a lexer because the only time you want the line number is on a parse error and those are generally both rare and not performance critical.

    (I trivially extended the approach so that my parser error messages give both the line number and the character position within the line, something that would have taken me a lot of additional work if I'd tried to be clever and efficient.)

I like interfaces but I haven't done enough clever things with them yet to have much to say. They do let me do almost all of the duck typing that I want to do in practice.

The 'switch { case <condition>: .. case <condition2>: ... }' idiom is something that I find alternately nice and perhaps questionable code style. To put it one way, they offer many opportunities to be quite clever.

(This entry is a bit scattershot because I'm a bit flat today.)

Sidebar: My view on Go strings, Unicode, and UTF-8

On the one hand it's kind of annoying that Go doesn't really have a 'Unicode string' type, which would represent strings as arrays of runes so that 'ustr[N]' was always a Unicode codepoint instead of possibly part of a UTF-8 sequence the way it is today with Go strings. Python offers such a type in its Unicode strings and they have a number of nice properties.

On the other hand, such a type is almost inevitably a lie given the presence of combining characters in Unicode. My understanding is that even with normalization there are codepoint sequences that represent a single logical character, ie there are not precomposed versions of all possible decomposed character combinations. I may be wrong here, but at least the issue is a swamp (and at least requires you to normalize your Unicode text, which is not necessarily a trivial operation).

Written on 22 June 2014.
« I need some responsive website design around here
Python 3 has already succeeded in the long run »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sun Jun 22 23:57:20 2014
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.