Saturday, December 15, 2018

1, 2, 3, Go!

OK, the title is an easy joke, but that's what happens when you give a silly name to a language… So I've started looking at Golang, since it seems to be the newish (ok, ok, not that new) kid on the block. And in fact the name is not an issue: I usually type "golang" to prefix my searches on the web and it works pretty well.

- I installed Go and the first striking thing are the instructions to read "how to write Go code". Having one way to structure the code (one workspace containing source control repositories, each containing packages) makes it very easy to get started - compared say to Java where different IDEs may have different approaches to what a "project" is. I wonder how this structure works when you write Go professionally, and you want to distinguish open sources dependencies from proprietary code. Also coming from Java where you're used to structure classes into packages, seeing Go repositories with a huge number of files inside one folder makes me shudder, but we'll see over time.
- I started of course with the Tour of Go. In a couple of evenings I went through it, proving how easy and small the base language is. Encouraging!
- I then started to get my hands dirty by writing a little app that hooks together JSON web services, Cassandra, Elastic Search and Docker. It was fairly easy to find libraries and manage to put them to use. I'll probably push that code to Github at some stage after some cleaning up.

So Go delivers on the promise to be a language easy to pick up and easy to get started. You can get from zero to productive in a few hours.

These are the features that stood up for me:

- Lack of generics. It's a trope in the computing world "Go doesn't have generics, ha ha ha". Of course straight away you see that arrays/slices and maps are typed, so already the basic use case for generics (safety in collections) is taken care of. Then you have functions as first-class citizens and interfaces, so there are plenty of techniques you can use to go around the lack of generics, so I'm not sure it's such a huge problem.
- Interfaces. It's interesting that there is no explicit declaration that a type implements an interface. If you define a Duck interface with a Quack method, every type that you use as the receiver for an implementation of a Quack function is considered a Duck. This is nice, but still tooling support to find out all types that implement a given interface will be a must ("OK, this function expects anything implementing Duck, what implementation can I use?").
- Pointers. I'm at the core a Java developer, so the idea of pointers makes me a bit uneasy. But I breathed easier when I saw "no pointer arithmetic". In fact, pointers in Go just make explicit if you pass a struct by value or by reference. I feel this adds a level of clarity that is worth the extra syntax and the initial repulsion the word "pointer" may awake in some.
- Errors. Go recommends a style where a function that may fail should return a tuple: normal value, error. The calling code can decide to ignore the error or deal with it. This is reminiscent of Either in Haskell. I'm still not sold that this is a better system than checked exceptions (but I seem to be the only one liking checked exceptions it seems), but I'll need more practice before I make my mind up.

So all in all I like a lot of design decisions that were made in Go, and it certainly makes for a pleasant programming experience. I ran into a few gotchas that I'll cover maybe in later posts, but nothing too stressful. So far, I have to say I enjoyed the experience!

Happy Go Hacking!


Thomas ten Cate said...

"I wonder how this structure works when you write Go professionally, and you want to distinguish open sources dependencies from proprietary code."

Yeah, this is annoying. Go was developed at Google, where all source code is kept in one huge repository, so there the model of a single GOROOT works well. For the rest of us, not so much.

Along the same lines, versioning. Google does not do it; they just build everything from HEAD. The rest of us needed a solution for versioning our third-party (or even first-party) dependencies.

This solution finally happened in Go 1.11 (24 August) in the form of modules: But I suppose much of the unofficial documentation you'll find online still does things "the old way".


I have been diving into Rust recently, and I'm finding it a much nicer language than Go overall. In a nutshell, Go feels like "a better C" while Rust feels like "a better C++". It addresses your points of concern almost exactly:

- Rust does generics really well, including constraints on the generic type (eat that, C++).

- Rust's traits (equivalent to Go's interfaces) must be implemented explicitly. Good for tooling, good for the human, at the cost of only slightly more code.

- Rust technically has pointers, but its main pointing device is the reference. The compiler guarantees that a reference always points to valid memory.

- Rust's approach to error handling is similar in spirit to Go's, but implemented better. Thanks to generics, a function can return an Option or a Result value instead of a raw tuple. The Option and Result types are chock full of useful methods to unwrap and chain them, and the "?" operator serves as syntactic sugar for unwrapping a Result safely (if it's an error, it returns that error from the current function).

Rust's focus is a bit different (more on memory safety, less on concurrency), but for those cases where both languages would fit the bill, I'd choose Rust any time.

JP Moresmau said...

Thanks Thomas for the insightful comments. I do plan to move onto Rust at a later stage.