I still haven’t written a thousand lines of Go; but what I have created does useful work and (considering I had to learn a language and a bunch of libraries along the way) didn’t burn that much time. Herewith another batch of programming-permanoob reportage.
Previously: Golang Diaries I.
Illustrated Love Letter · I’m a Web guy, and this speaks for itself:
resp, err := http.Get(uri)
if err != nil {
// horrible networking error, deal
return failure
}
if resp.StatusCode != 200 {
// grouchy server, deal
return failure
}
mediaType := resp.Header.Get("Content-Type")
if !strings.HasPrefix(mediaType, "application/json") {
// bogus media type, deal
return failure
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// body is a byte array with the server response
Isn’t it pathetic that so many allegedly-mainstream languages don’t have HTTP libraries that are this natural and idiomatic?
Other Good Stuff · Just like everyone says, Go makes concurrency a lot easier, without having to think about monads or actors or transactions. Just write simple linear code (like that HTTP stuff above) and farm it out with go-routines and channels.
Here’s sort of the core app logic in findIDP:
c := make(chan SearchResult)
go timeout(c)
go searchDNS(email, c, logger)
go searchWebfinger(email, c, logger)
go searchWebFist(email, c, logger)
go matchDomainPatterns(email, c, logger)
This is great... these all go do their thing and send the results back whenever they get them (the WebFist flow involves two different HTTP round-trips). Then I watch the results trickle in, bail on a time-out (currently 2 seconds, hmm), exit if I get an authoritative result, otherwise select from among the others based on a bunch of heuristics; I don’t spend a microsecond thinking about concurrency issues.
Some of the important virtues of Erlang here, without the dorky syntax and general weirdness.
Happiness · Matz wrote Ruby to make programmers happy, and it makes me happy. Go doesn’t particularly, but I’ll probably keep using it.
In Ruby code I find the smallest semantic gap between the image of the problem in my head and the code on my screen. In C, I find the smallest semantic gap between the code on the screen and my understanding of what the computer does.
Go gets real close to C in terms of the mapping onto the underlying machine. There’s no reason that Go shouldn’t be as fast as C, and given the quality of the team working on it, I’m sure it will be.
Go isn’t nearly as close as Ruby to the model in my head, but it’s a whole hell of a lot closer than C is. This seems to me like a decent trade-off.
I stumbled across Quotes About Go Programming at cat-v.org, and while the enthusiasts are kind of boring, I found I was resonating with some of the more balanced takes, such as If I had to describe Go with one word it’d be “sensible”. and Why would you have a language that is not theoretically exciting? Because it’s very useful.
Type -= Ceremony ·
Static typing is so much less painful when you have good type
inference, as Go has. I’m wondering how much of my cursing at Java
is really at all the declaration ceremony; it gets in the way of the work I’m
trying to do. Sometimes it’s helpful for readability to make a type visible,
and Go has the
var
facility for doing that.
And Last, the Gripes ·
It really irritates me, that in a modern programming
language, if I have an array full of type Foo
, and a function
that converts Foo
to Bar
, there isn’t a
map()
method that just gives me an array full of
Bar
. And then a separate concurrentMap()
, which
does the obvious.
Golang is sort of half-pregnant; it provides the incredibly-useful append() method, that appends to any type of array.
No, this is not a plea for generics! I just want some functional goodies.
Speaking of missing goodies, a little introspection would be nice. If you
glance at that findIDP code fragment above, it’s obvious that I have a lot of
different chunks of software that offer what you might call a
Searcher
interface. Why can’t I have them all in a directory
or package or somewhere and let the program figure out how many there are and
fire them all off? Quite possibly it can and I just haven’t got there yet.
Comment feed for ongoing:
From: Philip (Jul 15 2013, at 19:11)
Forgive my naïvete, but wouldn't this work as a simple, generic map function?
http://play.golang.org/p/jxMFq5UYs1
I'm not sure how it could be easily adapted to a concurrent model, but it seems to work for simple slices
[link]
From: Karl (Jul 15 2013, at 19:50)
Hi Tim
You should call defer resp.Body.Close()
Right after verifying that err != nil. As is, you'll likely leak goroutines for every non 200 status.
Also, if you know the content-length, using io.ReadFull is going to be much more memory efficient than the buffer-backed ioutil.ReadAll (http://openmymind.net/Go-Slices-And-The-Case-Of-The-Missing-Memory/)
Cheers,
Karl
[link]
From: Jim Lawless (Jul 15 2013, at 20:18)
Tim,
I'm afraid that I'm not well-versed in FP nor am I much more than a beginner with Go, but I wanted to comment on your mention of a map() function in Go.
If you define a slice as a variant type by using an empty interface ( interface{} ) you can then define a function that will operate on a slice of variants returning a slice of variants. Each element of the slice can then hold any type.
Please consider the code here:
http://play.golang.org/p/LvzwsrO7pU
In the main() function, I create a slice of three strings and apply the toIntTimesTwo() function on each element converting each from string to int, then doubling the value before that value is appended to a new slice. When the slices are displayed using the dumpSlice() function, you'll see that the slice of strings is converted to a slice of ints.
The myMap() function can accept a slice of interface{} elements and a reference to a function ( I hope I'm using the right nomenclature when I say "reference" ) that accepts one interface{} value and returns one interface{} value.
I don't think it's terribly pretty, but I think what you were referring to can be done.
[link]
From: Cedric (Jul 15 2013, at 20:58)
I think Go is well on its way to replace Python. I know, it sounds counter-intuitive, but every positive review I've read of Go came from Python (and to some extent, Ruby) developers. Bit of a surprise, but if a statically typed language gets Python developers to say things like "Because of Go, I haven't written a line of Python in six months", you know something is afoot.
Apart from that, I still cringe at Go's error handling with its "err, ok := Foo(); if err return failure" every other ten lines which brings back such bad memories of this kind of error handling (HRESULT nightmares, anyone?).
[link]
From: Joe Gregorio (Jul 15 2013, at 21:10)
"Why can’t I have them all in a directory or package or somewhere and let the program figure out how many there are and fire them all off?"
That's a matter for init() functions. See how RegisterFormat is used in the image handling library:
http://golang.org/pkg/image/#RegisterFormat
http://golang.org/src/pkg/image/jpeg/reader.go#L375
[link]
From: Joe Bob (Jul 15 2013, at 21:58)
Go is a higher productivity language that C for most tasks (systems, library, etc.) It's not a cure-all and won't replace X or Y completely, but it has a clear utility as being better than C for the majority of systems tasks. It would be interesting to see a large-scale OS kernel or database written primarily in Go.
[link]
From: Chris Walker (Jul 16 2013, at 01:15)
>> Why can’t I have them all in a directory or package or somewhere and let the program figure out how many there are and fire them all off? Quite possibly it can and I just haven’t got there yet.
You can do this in a slightly different way, by provideing a way for the "searchers" to register themselves. Then have each "Searcher" register itself in the "init" function of it's package/file, then use the "import for side-effects only" feature to include the package, which in turn will register your Searcher. e.g.
import _ "some/searcher"
[link]
From: Amelia (Jul 16 2013, at 03:56)
Philip,
Yes, that works, but we lose the strong typing, which is a pity.
- amelia
[link]
From: Jim Robinson (Jul 16 2013, at 06:28)
Were you aware of the
http://golang.org/pkg/mime/#ParseMediaType
function for parsing apart Content-Type?
Jim
[link]