My Thoughts About Go
After having used Go almost exclusively for the last two weeks, and for the first time seriously, I think I can give a general statement on what I think about it, analogous to D.
Go has been in development at Google for a few years now, mainly by Robert Griesemer, Rob Pike and Ken Thompson, all quite big names. You can find specifics on Wikipedia, this post will primarily be my opinion. My first impression is, while D feels like a child of C and Python, or “C++ done right”, Go feels a lot more like raw C modernized. Like with D, I will give you a first look at something simple:
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}
This all makes sense, right? The first line defines the package we are currently in. If this is a binary, no a library, this is always main
. We then import fmt
, the formatting standard library, enter main()
and print out our implicitly typed string. Nothing too crazy, and most of the rest of the language looks similarly boring. That is actually a good thing™.
It is comparably stripped down and intentionally minimal. The big features Go brings are Goroutines and channels, both tools primarily used for concurrency. Goroutines are essentially lighter versions of threads, that can be used in a shell-pipe-style way to produce data-pipelines. Channels are managed FIFOs, used by Goroutines to communicate. All of this works very well and is incredibly simple, making Go a fantastic language for handling lots of IO, like load balancers or maybe database connections. It just scales well. A Goroutine example:
func work() {
// do some work here
}
func main() {
// start 10 instances of work() in the background
for i := 0; i < 10; i++ {
go work()
}
// Note: because main() exits here, work() will just die with it unless
// we wait for it
}
But unless you are dependent on this extreme scalability, Go might not be the right choice for you. There are no native objects like in Python, D or other modern languages. At least they do not look like it. Objects are C-style typedefs, often structs, and methods can be attached in the following way:
type Vertex struct {
X, Y int
}
func (v *Vertex) String() string {
return fmt.Sprintf("Vertex: (%f, %f)", v.X, v.Y)
}
This is a Vertex method that takes no arguments and returns a string. Now, let us say the Vertex type should also implement an interface, because these exist in Go. It actually already does, it implements fmt.Stringer
. Why, you ask? Because it defined the String()
method, and interfaces are implemented implicitly as soon as all the methods they define are also defined by a type. This is one of the more extreme examples, where Go’s minimalism makes programming unnecessarily difficult.
On the other hand, Go comes with it’s own formatting tool, that enforces a standard coding style (but also tabs for indentation), package management (but without versioning, and the idea that you have to order your source in the so-called $GOPATH
, which causes more work than one would imagine) and build tools. Makefiles are not really needed anymore, unless you try to do something special.
Go has this overall approach of “back to the roots, but better”, but also “do it our way”. The minimalism works in most scenarios quite well, interfaces are the only real problem you will encounter while writing the software itself, and maybe the completely missing inheritance. Things like the package management will drive you nuts, though. And the fact that because of the way Go handles certain things, like visibility (everything starting with a capital letter is “exported”), cause you to having to type a lot more than in other languages.
TL;DR It feels boring, it feels old, and as a consequence overly laborious. As a general purpose language, I feel D does the job a lot better.