Tuesday, October 25, 2011

Go hack

I didn't post either of the last two Thursdays because each day I was too engaged with my hobby-work in the Go programming language to care about blogging. So it goes. After working with Go for a few months, I feel I've attained a feel for the language and can offer insights about what it does well and what it doesn't. In this post, I'd like to focus on one thing it does well: It makes hacking on the standard library easy.

That first Thursday I didn't post I was busy finding and fixing a nasty Go problem that suddenly manifested up on my laptop. In short, Go's WebSocket support stopped working. One minute my prototype web server handled WebSocket connections flawlessly, several minutes later WebSocket connections in Chrome failed to connect. In the minutes in between, I upgrade Chrome, so it looked like the problem was due to the browser. This surprised me. First of all, like many people, I've come to expect Google products to work as is (unlike certain other tech companies' products). Secondly, I upgraded Chrome as a regular apt-get update && apt-get upgrade Ubuntu update, which should include only fixes, not new features. Why then was Chrome upgraded from version 12 to 14?

In any event, I debugged my code and confirmed that the problem was indeed somewhere in the Go standard library websocket package, undoubtedly manifested because of a change in Chrome. WebSockets are new and not yet standardized, so such breakages are to be expected. The new Chrome implements a newer draft of the WebSocket protocol, and so Go needed a corresponding upgrade.

This is routine stuff for Go. You first install Go by pulling a local copy of the upstream Mercurial repository and locally building the compiler, library, etc., so updating the Go standard library is straightforward, entailing pulling new content from upstream and running a script to rebuild everything. However, pulling the latest from upstream failed to fix my WebSocket problem, so I had to switch from the stable release head (r60) to tip, which is where newest, experimental Go development occurs. I'm not a Mercurial user, having only recently gotten into learning distributed source control by way of git, so I'm a little shaky at switching heads. This time, after switching to the tip head, Go did not build. One of the standard library tests broke. What to do? The r60 head's WebSocket support was broken, and tip failed a test that maybe doesn't matter. I decided to play it safe and make a Frankstein Go. I copied the tip's websocket package into the r60 head and rebuilt everything. It worked: all tests passed, including the websocket's. And my prototype web server program worked again, too, now with the newer version of Chrome.

My point here is Go makes its internals easily accessible. I can't imagine making such a copy-and-paste-between-versions change to the C standard library and not end up spending a few days working through problems. Nor do I imagine this kind of fix would be easy in Python or many other nice languages. Partly Go benefits because nothing on my laptop uses it other than the few programs I write, so I can tinker with the Go standard library all I wish without system consequences. However, even if most of my OS were written in Go, Go makes it easy to sandbox development by having simultaneous Go installations.

But there's more to this. In no other language have I found myself daily reading through the language's source code to figure out how it makes things work. With Go I find it's useful to look inside the standard library. It's also easy and fast to insert Printf()'s inside the standard library to debug problems and work around incomplete documentation. Between the ease of (1) hacking on the standard library and (2) having a runtime that catches most runtime errors and prints stack traces and useful error messages, Go is unusual in that it uses pointers and bare access to memory heavily and yet I rarely find myself wishing to use a traditional debugger like gdb.

So the standard library and the development environment is something Go does well. But there are things it doesn't do well, too, and I've now spent enough time with the language to come to understand many of its limitations. (You don't really know a language until you can list a dozen problems with it.) Perhaps I'll write about those in the future.

2 comments:

Anonymous said...

What improvements would be made to tapestry.c if it were written in Go?

Craig Brandenburg said...

Anonymous

req := <- anonChan
if tapestryReq, ok := req.(*tapestryRequest); ok {
// TODO
}