Some of the most popular things on this blog have been diaries I’ve written as I explore my way through a new technology or hobby. I’m picking up the Go Programming Language, whose HQ is (significantly) at golang.org and which I’m going to refer to as “Golang” for reasons which should become obvious. Whatever you thing of Golang, it’s apparently got some momentum and some seriously smart people are working on and in it; so this may actually be of service to the community.
At this point, I should tip my hat to Mark Pilgrim, who taught me that a really good time to write about something is while you’re still discovering it, before you’re looking at it from the inside.
Big Gripes · My leading gripes with Golang right now probably aren’t the biggest problems with the language; rather, the biggest obstacles to learning it.
First, the name. “Go” has too many meanings and is among the world’s lousiest Google search disambiguators. After a few painful instances of searching for things like “go array literals” and “go repl” I got into the habit of sticking with Golang; and so will this diary.
The second big gripe is that Golang has neither an IDE nor a REPL. I don’t think, in 2013, that I should insult my readers’ intelligence by explaining why these are good things for a programming language to have.
I have sort of a REPL workaround, a little Golang program called
c.go
in which I can try coding idioms and type
go run c.go
; Golang’s compiler is very fast and its diagnostics
are really good. But it’s not a real REPL; I can’t build up exploratory state.
Illustrated Love Letter · So here’s a little code fragment that I’ll use to illustrate a few things about Golang that seem nice to me. Well, and some minor gripes too.
type Vector []float64
func (v Vector) MeanAndSignum() (mean float64, signum []int) {
total := 0.0
signum = make([]int, len(v))
for i, value := range v {
total += value
switch {
case value < 0.0: signum[i] = -1
case value == 0.0: signum[i] = 0
case value > 0.0: signum[i] = 1
}
}
mean = total / float64(len(v))
return
}
Let’s step through this line-by-line.
type Vector []float64
A Golang type
is not a class, it’s more
like a C typedef
. Which is easy to understand, and probably one
of the reasons why Golang is fast.
func (v Vector) MeanAndSignum() (mean float64, signum []int) {
There’s a lot of stuff to unpack here:
Golang isn’t object-oriented. So while nothing is an object, anything can
be the target of a method. The leading (v Vector)
means that if
you’ve got a Vector
named foo
, you can write
foo.MeanAndSignum()
to call this function. And that inside the body
of the function, you can refer to the receiver of the function as
v
; quite reminiscent of self
in Python.
The fact that MeanAndSignum
is capitalized
means that it’s exported; visible outside whatever package you’ve defined it
in.
The (mean float64, signum []int)
is really clever. It says
that this method returns two values (yes!) and simultaneously defines names
for them that you can use inside the function. Then when you
return
, whatever the current values are is what gets returned. I
like this a lot. This also illustrates Golang’s idiosyncratic declaration
rhythm (name then type) and even more idiosyncratic array-declaration
syntax.
total := 0.0
The :=
says I’m declaring a variable, and please infer its
type from the right-hand-side of the statement. I could also have said
var total float64 = 0
, but this is super-readable.
signum = make([]int, len(v))
I don’t have to declare signum
because I did that up in the
function declaration. make
is a built-in that allocates an empty
whatever; once again, I admit I haven’t entirely warmed up to the array
syntax. I’m also unconvinced that the distinction between :=
and
=
is essential; feels like unnecessary ceremony.
for i, value := range v {
Now this is really sweet. The for/range
construct gives you
both the array index (in i
), and the value (in
value
). Of course, there’s syntax for ignoring either.
Very handy.
total += value
Nice C-flavored code (there’s even ++) but look, Ma, no semicolons!
switch {
case value < 0.0: signum[i] = -1
case value == 0.0: signum[i] = 0
case value > 0.0: signum[i] = 1
}
Isn’t that a nice compact switch idiom? Definitely C-done-right. Of course, in some other languages switches are expressions that return values, sigh.
mean = total / float64(len(v))
Yeah, it’s statically typed, so you have casting. I’m not sure why it was a good idea to invent a new syntax, but this one seems OK. I’m also not sure why, since you have type inference, you can’t also have type auto-conversion, even Java would do the right thing without a cast. But no biggie.
return
See, because you named the return values in the declaration, you don’t need
to mention them explicitly here. But if you want you can have traditional
return
statements including the values.
Nits & Whining · Maybe I’ll come to realize eventually that these aren’t problems, but they’re irritating me at the moment.
Golang will not let you compile if there’s an import or declared variable that’s unused. OK, I understand that Cleanliness Is Next To Godliness, but this is seriously slowing me down; probably a third of my attempts to run my damn program fail because I’ve forgotten to remove debugging apparatus, or I’ve sketched in a variable but not used it yet. How about a compiler switch or something?
There doesn’t seem to be a way to declare a constant array, what in a
Java class I’d call final static String[] COLS = {"Red",
"Blue"};
In fact, I still find array declarations and literals egregiously
weird, for example [...]
. I’m really hoping it starts to feel
natural, because I’ve rarely managed to type in an array declaration right
first time so far.
Responsive! · I filed a bug on June 6th, and it was fixed on the 12th. Gotta love that.
Comment feed for ongoing:
From: Stefan Tilkov (Jun 15 2013, at 11:03)
That "taught me" link to https://www.tbray.org/ongoing/When/201x/2013/06/16/the%20best is dead.
[link]
From: Kamil Kisiel (Jun 15 2013, at 11:28)
Go doesn't have casting, it only has type conversion. Then you say float64(len(v)) you are actually creating a new value of type float64 that's a copy of the value returned by len(v). I imagine the different syntax helps to make that more obvious.
[link]
From: Senthil Vaiyapuri (Jun 15 2013, at 11:31)
Hi Tim,
The 'taught me' link results in a 404. Please fix.
Thanks and Regards,
-senthil
[link]
From: John Cowan (Jun 15 2013, at 11:33)
It's really C that's idiosyncratic, with its "declare it like you use it" syntax, although way too many languages have copied it. Go uses the Algol and Ada style, only with less punctuation, and is instantly readable as English. In particular, "func (v Vector) MeanAndSignum() (mean float64, signum []int)" reads as "a function returning 'v', a Vector, named 'MeanAndSignum', with an argument 'mean', a float64, and 'signum', an array of ints." Nothing could be finer. Similarly, "make([]int, len(v))" means "make an array of ints whose size is len(v)"; you could argue that the order of arguments is wrong, I suppose. Finally, casting is conceptually just calling a procedure with the right name, much better than C's messy casts (which got their parens because Algol 68 had the wrong priority of operators to omit them reliably in that language.
[link]
From: ebenezer (Jun 15 2013, at 11:57)
Unrelated to the discussion: “taught me” in the second paragraph is a broken link. :)
[link]
From: Aalok Shah (Jun 15 2013, at 12:57)
//Here's a way to do initialization of an array
package main
import "fmt"
import "strings"
func main() {
v := []string{"Hi", "how", "are", "you", "doing", "today?"}
fmt.Println(strings.Join(v, " "))
}
[link]
From: Tim (Jun 15 2013, at 13:07)
Oops, thanks, fixed the broken link.
[link]
From: Daniel (Jun 15 2013, at 14:36)
@John Cowan
"... instantly readable as English ..."
And then you read it out in a different way than Tim. I don't know which is right, and that means the above quote might not be entirely true ...
[link]
From: John Cowan (Jun 15 2013, at 15:43)
Daniel: TMTOWTDI, fine.
[link]
From: nnutter (Jun 15 2013, at 18:04)
@Daniel and John
John's "English" interpretation of the function definition is incorrect; it's not a case of TMTOWTDI but the sentiment is correct. The English would be:
A function for v, a Vector, called MeanAndSignum that takes no arguments and returns mean, a float64, and signum, an array of ints.
[link]
From: f1 (Jun 15 2013, at 20:53)
Just call it "Go" and tag your blog post once with "golang".
A final array in Java is not constant:
static final String S[] = {"a", "b", "c"};
S[0] = "d";
[link]
From: John Cowan (Jun 15 2013, at 21:11)
Thanks for the correction. Post in haste, repent at leisure.
[link]
From: Walter Underwood (Jun 15 2013, at 23:27)
I do wonder how syntax ideas get lost.
I used that kind of switch statement in PL/I back in roughly 1975.
And it is nice to see the ALGOL68 declaration syntax show up again.
What's next, VALOF from BCPL?
[link]
From: Dmitry Vyukov (Jun 16 2013, at 04:10)
It would be interesting to see how well Golang performs on WideFinder II. Golang looks near ideal for parallel logs processing... but WF is dead now, right? Main Golang toolchain is not yet ported to SPARC/Solaris, though, probably gccgo will do.
[link]
From: Alex Boisvert (Jun 16 2013, at 09:00)
Thank you for this very readable blog post. I liked how you "unpacked" each line and gave commentary on the language's design choices.
[link]
From: Nerd (Jun 16 2013, at 10:35)
Here's your REPL (ish):
https://github.com/pope/ob-go
#+begin_src go :imports "fmt"
fmt.Println("Hello World!")
#+end_src
#+RESULTS:
: Hello World!
[link]
From: Anthony Starks (Jun 16 2013, at 11:30)
Good to see you trying Go. FYI, your examples use slices. Arrays and slices are distinct:
http://golang.org/ref/spec#Array_types
http://golang.org/ref/spec#Slice_types
[link]
From: Cory K (Jun 16 2013, at 15:18)
I've been using Go since the first public release, and I had a few of the same irritations when I started. A few years removed they've mostly disappeared, although I can't say if that's because they were invalid or I've just become acclimated.
Lack of IDE: I use emacs with gocode and go-flymake. It's about as IDEish as I could want.
Lack of REPL: Most things I would use a repl for I can accomplish with play.golang.org. I do not know if my experience is common, but much of the time I'm exploring in a dynamically typed language's repl, what I'm really trying to determine is what functions expect and return.
Explicit casting: This was a very deliberate decision by the Go designers. It's saved my butt from truncation bugs several times. My only remaining complaint is that the compiler should be more context aware when deciding on types for numeric literals.
[link]
From: Jens Rantil (Jun 17 2013, at 03:47)
Well, it seem like they really didn't fix your filed bug report correctly in 6 days... http://code.google.com/p/go/issues/detail?id=5655#c3
[link]
From: karolis (Jun 17 2013, at 04:10)
Sublime Text + GoSublime works just as good as any 2gb IDE you'd want
[link]
From: Bernd (Jun 17 2013, at 04:26)
There is no REPL for Golang because Golang is not an interpreted language. Go code has to be compiled before you can execute it.
Further more, if you write something like:
mystring := "Hello REPL"
In your non existing REPL then golang will raise an error because mystring was defined but not used.
[link]
From: Jon Renner (Jun 17 2013, at 06:00)
As f1 mentioned, a final array in Java is still modifiable
[link]
From: Bobby Powers (Jun 17 2013, at 06:56)
while you're developing if you find yourself adding or removing calls to 'log.Printf', and having to add/remove import lines, you can add:
var _ = log.Printf
After your import block. See http://play.golang.org/p/0YawM32txp for example. 'var _ = $x' is a fairly common go idiom for saying 'I plan on using $x in the future, don't worry about it compiler'.
[link]
From: Justin Heyes-Jones (Jun 17 2013, at 06:58)
On the missing IDE, I've found that emacs with Go autocomplete, gofmt and goflymake make a quite usable combination. Gdb is well supported but I haven't needed to graduate from prints debugging to try it, yet.
[link]
From: Julian Morrison (Jun 17 2013, at 07:31)
[3]foo is an array of length 3
[]foo is a slice (in C, a struct containing len, cap and a pointer to the array)
[3]foo{a,b,c} is an array literal
[...]foo{a,b,c} is an array literal, of type [3]foo (inferred from the number of entries)
[]foo{a,b,c} is a slice literal, len 3, cap 3, underlying array length 3
I hope that makes the arrays seem a bit more sensible.
[link]
From: frog (Jun 17 2013, at 08:36)
One of the best parts about golang is the compile time and that is partly due to the syntax. i watched a presentation given by rob pike and he explains the reasoning behind the syntax. http://www.infoq.com/presentations/Go-Google
[link]
From: Russell Beattie (Jun 17 2013, at 10:21)
Wait until you start trying to parse real-world unfamiliar XML (like feeds with random namespaces) and JSON. Your nits and whinging section might expand a bit. ;-) The libraries are pretty clunky, but that's mostly because map[string]interface{} is essentially the only dynamic data structure in Go and it's an incredible pain to use as there's no generics and/or inheritance (interface{} is a type, not an object). After using Python's dict and JavaScript's uber-flexible objects, it's a hard transition. That said, the low memory footprint, general speed and ease of go routines (with no GIL) makes it worth the pain.
[link]
From: Sriram Srinivasan (Jun 18 2013, at 00:01)
Hi Tim,
Here's a local golang playground: https://github.com/sriram-srinivasan/gore
[link]
From: John Cowan (Jun 18 2013, at 15:32)
Bernd: There are plenty of REPLs, notably for SML/NJ and for the Scheme dialects Kawa and Guile, that are read-compile-execute-print-loops.
[link]
From: Anon (Jun 21 2013, at 08:43)
liteide is awesome: https://code.google.com/p/liteide
[link]