Go after four months #golang
Summary
Update Nov 2021: I spent almost a decade doing Go full time and I still love it. I could focus on the problem I was solving, not the language. It’s fast, stable, and easy to read, but it was time to move on. I’ve being doing Rust full time for the past year.
Update July 2015: I’ve now been working with Go full-time for over two years, building a multi-protocol instant messaging system for finance. Apart from early problems with slow garbage-collection (don’t cache everything in-process), I have nothing but good things to say about Go. It kept our code simple, and our server fast and stable.
I’ve been using the Go programming language for over four months in my spare time, mainly to write an IRC client (hatcog) – here is my trip report.
Go sits somewhere between C and Python. It has the static type checking and bit-twiddling powers of C (and pointers!), yet much of the speed of development and conciseness of Python (e.g. string split and join). Bruce Eckel called it a language designed to create servers, and I agree wholeheartedly.
Go is a C family language, both in the sense that it looks like C, and in the sense that you can easily call C libraries. Here is hello world:
package main;
import "fmt"
func main() {
fmt.Println("Hello World!");
}
Interlude: Cooking up Go
This is what I think the recipe for Go looked like:
-
Go back to the 70s / 80s and find a bunch of great programmers. Say, Ken Thompson, Rob Pike, people like that. Marinate them in Bell Labs for 30 years, during which time they code in C, keep developing Unix, invent Plan 9, UTF-8, and other wonderful things.
-
Take them out, seduce them with Python, wow them with Google-scale computing. Add more amazing programmers (Brad Fitzpatrick for example), stir in Google’s near-unlimited resources.
-
Ask them how they would do C now, if they could start from scratch.
Observations
The C legacy is very strong in Go, and not just in the curly braces. For example object-orientation is done with a struct plus some methods.
Capitalization is significant. The visibility of a name outside a package is determined by whether its first character is upper case.
Multiple return values instead of exceptions. To signal an error occurred in your function you return the error, like in C, but you can return multiple values, like in Python. Error checking typically looks like this:
retval, err := myFunc(param)
if err != nil {
// handle it
}
The style guide is whatever program gofmt says it is. It will often just fix it for you. Use whatever style you want, then run it through gofmt before checking-in.
Interfaces, with no explicit implementations. If you have the methods of that interface, you count. That gives statically checked duck typing. Lovely.
Go has arrays, but you usually use slices instead. A slice works like a Python slice, but internally it’s a smart pointer to an array, and has a different type (which matters because we’re doing static typing remember). The array is it’s backing storage. I found this tricky to understand at first.
Everything is passed by value: Objects (structs) and Arrays are copied before being passed. For arrays it doesn’t matter because you’re usually passing slices (which wrap pointers – as do maps). When defining your own types, you have to remember to always pass a pointer.
The compiler is strict: Unused variables are a compiler error.
Negatives
The one big potential negative is that it’s compiled. In Python I often find myself stepping into third party libraries. Always having the source code is fantastic. One print statement deep in a someone else’s code can really help comprehension. No being able to do this in Java was a problem. I don’t have enough experience with Go to say if this will be a problem during debugging or not.
A read-eval-print loop would have made learning Go much easier. I can’t overstate how often I try out simple commands, like date formatting, in ipython. In Java I used beanshell.
You can’t daemonize, because of the way go-routines work. This could be fixed soon, see Russ Cox’s message here. In practice I usually use Upstart, so it doesn’t cause a problem.
Go has an un-googlable name. The convention is to use golang as a search term.
The language is still very young and in flux. See for example the GitHub network for gocurse. v1 of Go is due in the first half of this year (2012), so hopefully things will settle down.
The extremely useful string manipulation methods are all in the strings package, instead of being methods on the string object. There are objects in many packages, so there is no doubt a very good reason for string not having these methods. That’s the thing about Go, there’s usually a very good reason for everything it does. Learning Go is an education. Oh, that’s a positive.
Positives
Old programs read like quiet conversations between a well-spoken research worker and a well-studied mechanical colleague, not as a debate with a compiler – Dick Gabriel.
Russ Cox uses that Dick Gabriel quote to illustrate what they are aiming for with Go. In some hard-to-define way, Go feels serious and grown up. It has high expectations of you.
A familiar language. As a Python programmer with a Java and C background, I was productive very fast.
A modern language. Go has everything you’d expect a modern language to have: Unicode, garbage collection, multi-core (GOMAXPROCS), built-in map, string and array types, closures, unit-testing , an official style guide (‘gofmt’), reflection, etc
The documentation is excellent, and usually available from the command line: godoc <package>
.
The compiler is very fast, fast enough that you can use Go as a scripting language. Use gorun as your hash-bang line.
Static typing makes programming easier. That is a debatable point, but it does for me. Static typing makes code easier to read, and make me feel a little more comfortable that things won’t go boom in the middle of the night.
Concurrency
The one big NEW (to me at least) thing Go does very well is concurrency. The keyword go
starts a new go-routine. That’s what you use instead of sub-processes, threads, or co-routines. The go-routine might run on the same core / processor as your current one, or it might not. You don’t worry about that.
Go-routine’s communicate on channels. You epoll those channels using the select
keyword.
Here is the heart of hatcog, my IRC client. A go-routine listens on the port connected to the server and puts incoming data on channel fromServer
. Another go-routine listens for keyboard input, and puts that on channel fromUser
. The middle bit watches both channels:
for self.isRunning {
select {
case serverData, ok = <-fromServer:
if ok {
self.onServer(serverData)
} else {
self.isRunning = false
}
case userInput, ok = <-fromUser:
if ok {
self.onUser(userInput)
} else {
self.isRunning = false
}
}
}
More details on Go’s concurrency model
Do you see how that makes servers so much easier? You could write a webapp that’s it’s own server. The “C10K problem” might not be a problem at all.
Conclusion
Would I be happy working with Go as my main language? Yes, I would. It’s a joy to work with, and I got productive very fast.
Am I using it instead of Python for all my new projects? No, I’m not. There are two reasons for that. Firstly, it’s a very young language, so library availability is limited (for example, I need curses). Second, well, Python is just so amazing.
If I was doing Java or C++, I would invest heavily in Go. It was designed to replace them, and it does that well.
As a Python guy, the summary is more nuanced. I see the benefit of static typing. Other claimed benefits of Go over Python are that it’s faster, and that it’s “better at scale”.
For some things I’ve done Go has been faster, for other things Python. The biggest difference is probably in how well I write the code. Although I love speed in principle, nothing I do is CPU bound.
The “better at scale” argument doesn’t really apply to what I do for a living, which is building webapps with Django. We scale performance-wise by adding servers, and code-wise by adding small self-contained ‘apps’ (in the Django sense).
Go’s sweet spot is building servers. It makes concurrency safer and easier than the current options. I’m going to keep using it for that. And because it’s fun.
Next you might want to page through this Go presentation, or dive in to the (rather academic) tutorial.
(Did I make a mistake? Please correct me in the comments. Thanks!)