About Articles Projects Links Apps Feed

Go: A Short Review

This article relates my opinion on the pros and cons of the Go programming language. In short: the pros are overwhelming while the cons are merely points of contentions.

Official documentation

The official documentation is fairly complete and well organized. It is recommended to go through the following documentation, in order:

  • A Tour of Go (a.k.a. go-tour)
  • Effective Go
  • Frequently Asked Questions (FAQ)

The go-tour is an interactive introduction that covers most of the concepts of the language. It features a few challenging exercises. One of them is a virtual web-crawler: the challenge is to implement a recursive goroutine that needs to synchronize with all its instances. Here is my suggested solution.

Pros

  • Almost no dark corners (see below).
  • Minimalist syntax (e.g. no more parenthesis for if and for, optional semicolons).
  • The syntax is rigid enough to put an end to the never-ending C-style wars: the opening curly brace must be on the same line, one-line control structures must have braces.
  • Local control structure declarations, e.g.

    if _, status := f(); status { ...
    
  • Universal UTF-8 support.
  • Strong typing. Type names are clear, size can be relative or absolute (e.g. int or int8), the C char is the more sensical Go byte, rune is used for unicode characters.
  • Slices, maps, and channels are compound types (i.e. they are actually pointers), all the other base types are atomic (strings included). C-style strings are still possible using static arrays. These simple rules makes it easy to grasp memory usage.
  • Garbage collector: nothing very new here, except that the simple aforementioned rules on compound types makes it easy to exploit it correctly and efficiently.
  • Nearly everything is explicit; as for C, we can almost guess the generated assembly code in most cases.
  • The standard library is very complete: checksums, file system management, serialization, big numbers, etc.
  • External calls à-la execve are safe (it does not make use of a subshell, it uses a string list to pass arguments) and support both redirections and return code. Many languages have this feature, but this is important to point out since it is a big issue for those which do not, like Lua or POSIX shell.
  • Multiple return values are supported. This is a big plus for error management.
  • No exceptions (in general): very big plus for error management.
  • The defer keyword: an elegant goto that allows execution of code before a function returns (such as closing file descriptors). It is essential for writing a clear and clean function termination.
  • Package import is as simple as it can be, as is the hierarchy of projects. No protected members, no headers, function order does not matter, and so on.
  • Performance: Go is a serious competitor to C and Fortran!
  • No object orientation! Perhaps one of the strongest point of the language.
  • Interface: allows for the genericity of object orientation with only one new type-concept, and without hierarchy (flat relations through composition).
  • Concurrency: one of the main goal of Go is to offer a seamless support for asynchronous concurrency via its goroutines and channels. This remains to me the simplest concurrent programming experience I’ve had. A very welcomed feature.
  • Distribution tools: go can download source packages, compile, install and execute.
    • It is possible to run go files with go run just as if it were an interpreter. The source file actually gets compiled and the resulting executable gets deleted upon termination.
    • go install analyses the imports: for every package that is not to be found locally, it will automatically download it if it is a URL, then compile and install. Various version control systems such as git and Mercurial are supported. In a way, go is also a package manager (like Luarocks, pip, rubygems, etc.). Having a standard package manager is a big advantage for centralizing community contributions. The lack of such a tool has long been a major flaw of Lua and still is a burden for C.
    • go only compiles source files that are more recent than binaries. Farewell, Makefiles! Something less to care about.
    • go links statically by default. It is quite opposed to the current trend. See cat-v.org and here for an (anti-)rationale.
    • A Go tree is very simple:

      /bin
      /pkg/<arch>/<source|author>/<packages>.a ## static libs
      /src/<source|author>/<packages>.go
      
    • gofmt is an automatic code formatter. The fact that it is part of the standard distribution ensures some universality in style. It puts an end to never-ending debates on style issues that have no right solution.
    • godoc is a simple yet powerful documentation system. The lack of markup makes it easy to use.
    • go test is a testing system. Again, very simple: run go test over a package and every filename ending in _test.go will be run. This keeps the file hierarchy simple. Having this part of the standard distribution makes it easier and more automatic to integrate tests.
  • Go 1 Compatibility Guarantee ensures that Go 1 program will always build with the latest Go distribution. Considering this together with static binaries, a Go 1 program will never fail to start either. This is a gift in a world where programs tend to be very fragile in regard to library updates. (Python is one of numerous examples.)

Points of contention and other dark corners

  • Debugging capabilities are yet to improve. (See http://golang.org/doc/gdb.) Nonetheless the standard distribution features some nice tracing and profiling tools. Most importantly, Go binaries support displaying a backtrace on crashes, thus freeing Go from one of the main needs for a debugger.
  • Strings are defined as immutable byte slices and behave as such except for the range keyword which loops over the runes of the string, and not the bytes. This behaviour has been adopted for performance reasons by many modern languages. Indeed, rune indexing needs linear time while byte indexing runs in constant time; on the other hand, a range is performed linearly over the whole slice, and thus no time is wasted returning the runes instead of the less useful bytes. While this design choice is optimal in my opinion, the documentation should emphasize this to the beginners as it can be misleading at first.
  • Members starting with an uppercase letter are automatically exported. On the one hand it exposes the visibility of the objects very clearly, everywhere. It also enforces a naming style. One the other hand, if you want to change the visibility of a member, you need to refactor all the calls to it in the package. An export keyword would have saved the effort.
  • Go has no operator overloading ability. This has led to long debates and it is hard to tell what the right choice is. It makes Go not so convenient to write code for big numbers and matrices.
  • The Go standard library boasts a simple and elegant design overall: only the most useful and hard to implement methods are present. The rest can be easily implemented if needs be, possibly using those initial methods. However, a few packages show little use and could have been left out (e.g. list), while some have too many methods in my opinion (e.g. strings).
  • Go allows shadowing functions and types, even the basic ones. On the one hand, it removes any name restriction on short-lived variables within restricted scopes (functions, blocks…). On the other hand, it paves the way for obscure compilation errors.
  • Go’s strict typing is a blessing and does not allow for automatic conversions… but for one exception:

    x’s type V and T have identical underlying types and at least one of V or T is not a named type.

    D. Honnef mentions this oddity and provides one explanation.

Conclusion

In my opinion Go is a versatile language that is suitable to most contexts. One exception would be very low-level programming with important memory constraints (although Go allows for fine tuning of the garbage collector). Go is definitely ideal for desktop and server development.

Go is concise, technically simple and clear, pleasant to read and write, rigorous and reliable, supported by very smart tools, modern in the way that it features much of the useful progress made in language expressiveness. To top it all, its implementation is extremely efficient.

I am not sure how convenient it is in a context of math or computer graphics: it is debatable that the lack of operator overloading is detrimental.

Go’s main goal is not expressiveness though: languages such as Lua and Lisp are much more capable in this field thanks to a powerful introspection design. (Lua has metatables, Lisp is homoiconic.) This high-level expressiveness can be treacherous at the same time and I would not recommend it for team development: the risk to write unreadable code is just too high. The lack of advanced introspection capabilities makes of Go a very solid language by design.

References

Comments

Date: 2014-12-01 (Last update: 2018-08-14)

Made with Emacs 27.0.50 (Org mode 9.1.9)

Creative Commons License