As part of my continuing self-education and in following the advice of the Pragmatic Programmers to learn at least one new programming language per year, I've been messing around with Go in the evenings for the past few weeks. I've been working through the tour and trying out various exercises suggested there.
I'm a little ambivalent about Go as of now. The language has some really cool features that I like such as multiple return values and goroutines but I find the general structure of Go programs a big departure from what I'm used to in Java. Frankly, I find a Go program's structure a bit disconcerting.
I learned Python a couple of years ago and Go programs seem to have an even less cohesive structure than Python. In Python, you put methods under a class heading but you still have to specify self as the first parameter to the functions/methods def'd under it. In Go, the func definitions just have to be in the same package as the data structure(s) that they use and operate on—at least that's my impression for now, which of course may still be wrong at this point. You still have to explicitly "bind" a func to an associated data structure by declaring the data struct as a "receiver" in the func header. Then you still have to explicitly use the receiver's reference inside the func. In Go, there doesn't seem to be a notion of "this" or "self" as there is in Java and Python, respectively. There also isn't an implicit association between a set of data/fields with the funcs/methods that operate on them that's based on the program structure. My impression when I read a Go program is that you just have a bunch of loose struct and func definitions and you have to manually/explicitly tie these things together.
I'll explain what I mean with an example in the next post below.
So, I was messing around with the example implementation of Conway's Game of Life. The original code is here: https://golang.org/doc/play/life.go and I was messing around with trying to refactor the main() func, the original of which is shown below for convenience:
The code starting at line 14 in the above listing defines the type Life struct. On line 23, the func (l *Life) Step() declares its "receiver" as something (an object[?]) with a type of Life and that can be accessed using the pointer named l. I didn't like the one-letter name, especially since it also looks like the digit 1 so I thought I'd change it to this or self but on digging around for style guides for Go, I guess a one-letter reference is pretty common. I still don't like the name looking like a digit but I decided to go (pun intended) with the flow for now and conform.
This example shows what I mean about the program parts being very loosely associated with each other. It doesn't seem very intuitive. When I read the code for the first, I didn't get the same kind of sense of hierarchical organization of the program parts like we would with a Java program: a class containing field and method members which in turn contain their own things. In Go, everything just seems to be floating around and tied together with vague references to other things in the same package. This is going to take this Java programmer a little getting used to.
Like I said previously, I was messing around with the Conway's Game of Life code and trying to refactor it to my tastes. I was wondering if someone would mind giving feedback on the changes I made. Are they too "That's what a Java programmer would do but Go programmers don't do that kind of thing."? I'm curious because when I was learning Python, I'd often see comments like "Python is NOT Java!" and took that to mean "We don't do that kind of thing in Python!"
Here's what I came up with:
In summary, my changes were:
1. Added code so that the program would take two parameter: the number of generations to simulate and a number representing the fraction of a second to delay before displaying the next generation of the simulation.
2. Added a variable g in the type Life struct on line 18. The new variable is to track how many generations have been calculated since the object was created.
3. Extracted the code originally in main to the Show() and Simulate()
4. Added a statement in func Step() to increment the new generation count variable
Here's another thing about the Go language syntax rules. In Go, you can't do this in trying to format the method call to span multiple lines, with the closing parenthesis on a line by itself:
The compiler error message caused by line 4 is: syntax error: unexpected semicolon or newline, expecting comma or )
To fix that, you have to do either this:
The extra comma at the end of line 4 in Version 2 above, which would otherwise cause a compiler error in Java, is legal in Go and acts like a line continuation character. This is handy when you want to format your code to occupy multiple lines so that it's easier to tell the different parameters apart. It's also easier when you are passing in a variable length list of parameters, say for testing purposes, and you just want to copy/paste lines of values to add more parameters to the call.
This reminded me of when I attended a keynote given by Rob Pike of Google at the SPLASH conference in Tucson, AZ where he talked about the Go language. He mentioned this feature of Go, citing the convenience of being able to just copy/paste a bunch of lines to add more values to a list without having to worry about whether or not you had exactly the right number of commas needed. Apparently, the annoying little compiler error that most Java programmers are used to dealing when there are missing or extra commas in a list of values was just too much for the designers of Go. So, they made it legal Go syntax to have that extra "," at the end of the line as long as the next non-whitespace character was the closing parenthesis. You are allowed only one extra comma though and it had to be used like a line continuation character.
Junilu Lacar wrote: Are they too "That's what a Java programmer would do but Go programmers don't do that kind of thing."? I'm curious because when I was learning Python, I'd often see comments like "Python is NOT Java!" and took that to mean "We don't do that kind of thing in Python!"
Well, GO is not Java, either. And I think the Python folks are really saying "that is not idiomatic Python"
not that "we refuse to do that kind of thing in Python" or even "no one does that style of code in Python"
I'm no GO expert, but yeah, I think your code is still not idiomatic GO. From what I've seen (and I can't write good idiomatic GO either)
most good go is more functional and makes more use of channels. For example, this video from Rob Pike shows using channels for coroutines. The usage is subtle and nearly invisible.
Rob Pike on channels for coroutines
the designers of go are very smart and very experienced. They left out a lot of stuff on purpose, like the focus on objects and inheritance. Based on my Java time, I think that the OO fad has run its course.