12 Sep 2015 Faster, Higher, Stronger! Go Go Go!
It was a Friday and I was thinking about what to do on the weekend. My better half was going shopping with her friends, and I was choosing between bike riding on the Great Ocean Road or having a garage party with friends.
Then, just before I was going to shut my laptop, I took a quick look at Reddit and saw a post about a challenge that the guys from Go Challenge run each week.
So that’s what I decided to do that weekend…and the next 3.
What is Go?
Go is a programming language that was developed by Google and released in 2009. Whilst it’s pretty new, it’s also production ready. The most well-known example of its real-world usage is Docker.
Go is a compiled, statically-typed language with automatic memory management (using a garbage collector). My personal first feeling was “WOW, it’s C on steroids”. The building blocks are the same – structures and functions. The additional constructions are packages, interfaces and functions’ receivers. Also, a function is an object and you can utilize FP paradigms as well. Moreover, you can specify function signatures as arguments and return types.
The structure of packages is similar to Java, meaning that it’s a tree structure where each folder is a package. However, encapsulation can be done only on package level. There are only two types of visibility:
- Public (exported) – any definition started with capital letter. Public definitions are accessible from other packages;
- Private (not exported) – any definition not started with capital letter.
//Package foo provides shows //how encapsulation works. package foo //CounterHolder is exported and //accessible outside foo package. type CounterHolder struct { counter int } var ( //internalCounter is private and //reached only from foo package internalCounter = new(CounterHolder) ) //Increment is exported and //visible outside the package func Increment() int { internalCounter.counter++ return internalCounter.counter }
Some other major highlights of the language:
- Type inference (x := 0): You don’t need to declare a type if it can be inferred from an expression
- Interfaces: You can declare interfaces like in Java, but you don’t need to explicitly declare that a type “implements” an interface. Instead, any object that has all the methods of an interface is considered to implement it. For example:
package foo import( "fmt" ) //Bar is interface that define one method //SayHello() type Bar interface { SayHello() string } //BarImpl is implementation of Bar. //We don't specify interface anywhere. //Any type with method SayHello() will be a proper implementation. type BarImpl struct {} func (impl *BarImpl) SayHello() string { return "Hi everybody\n" } //Pass interface as argument func SayIt(b Bar) { fmt.Print(b.SayHello()) } func main() { //Using BarImpl SayIt(new(BarImpl)) }
- Remote package management: It works very similar to npm. There is a go-get tool that will download and install package for you.
- Rich embedded API: Includes powerful concurrency primitives (routines, channels,…), HTTP, JSON/XML, templating, crypto, images and more.
- By default, it produces statically linked binaries (bye bye shared libraries versions hell)
- Supporting iOS (from v.1.5) and Android (from v. 1.4)
- The Unix philosophy in its language environment. This is one of the best strengths, in my opinion. Go provides a lot of different tools that can be integrated with IDEs using hooks. For example, gofmt formats sources, govet searches for suspicious constructs, golint checks code style and gorename is for renaming identifiers across a whole project. Each tool only does one specific operation. As the result, all tools are very quick and really straightforward in usage.
What do I need to start?
The docs are the best place to start. Then just install Go or work your way through the online interactive introduction ‘A Tour of Go‘. I also recommend using the Golang Docker images. For me these were the easiest way to run the latest version on Linux.
Another doc that I highly recommend to keep open while developing is Effective Go. It helped me a lot when I was struggling with constructions that were new for me.
Finally, there are these communication channels:
- Google group – https://groups.google.com/forum/#!forum/golang-nuts
- IRC Channel – #go-nuts on irc.freenode.net
- Slack Channel – https://gophers.slack.com/
What was my first Go Challenge?
My first Go Challenge task (although it was actually the fifth in the series) involved creating a tool that could find all public symbols that are not being used and converting them to be private (in Go, any definitions that start with a capital letter are public). Also, it had to prevent naming conflicts that might come up during the renaming.
I broke down the problem to the following sub-tasks:
- Get all source files from the package
- Find all definitions with usages
- Sort definitions that are not used or used only internally
- Rename all definitions (replacing the first capital letter with lower case) and usages that could be renamed without conflicts
- Provide command line interface to run the application
This particular Go Challenge was intended as a workout of the new go/types package that was released in version 1.5. Using this package you can check source code and extract information about types, definitions, usages. Whilst it mightn’t sound like I had to do much, the output API is very low level, meaning I had to create my own internal structures and wire up a lot of pieces.
The most difficult part was function arguments that had a type of some interface. I was stuck couple of times and even ended up raising an issue in Go’s Github project. It turned out not to be an issue after all, but I got a very quick and useful response (big personal thanks to Robert Griesemer). I took this as evidence that the community is functioning well and it’s easy to find answers to questions.
Sadly, I didn’t win that week’s Go Challenge, and if you compare my code with that of the winners (Robert Hovarth and Fatih Arslan) then it becomes obvious why. The main problem was that my code is basically like Java code written on Go. In contrast, the winning solution is more compact, but not so much that it becomes excessively complex. Clearly I need more practice writing Go code!
My First Impressions
Here are my first-impressions about Go compared to what I normally using everyday (Java, JS, C):
- Easy to start: At first glance, I had supposed that it would be more of a C-like a nightmare of compilers and shared libraries. But it was really comfy, like Java. Docker helps a lot as well.
- Tools are awesome: Whilst a golang plugin is already available for Intellij, I decided to try it with Atom and Sublime. These hooked into Go tools like govet, gofmt, golint, and gorename, and worked really well. Adopting the Unix philosophy to develop a language environment is a totally brilliant idea.
- Docs are good: I didn’t have any problems when starting to develop . The examples are super handy and it was an awesome idea to store them in code that actually gets compiled! It makes it easy to keep support documentation up to date.
- Tests: Good and easy. Built in coverage measurement is also a really nice bonus. Also, in addition to tests, you can add examples that will be included in docs for your packages and benchmarks.
- Error handling: I think this is only one thing that I didn’t really like. I remember that it was a pain point in C, and Go took me back to that time. I know that try-catch is not really nice from a performance perspective and not always clear semantically, but I think it’s a more convenient mechanism than having to process each error separately. Just, to illustrate, the function below is reading a file, jumping to a specific position and replacing a word. It does this by saving everything after the word into a buffer, truncating the output, writing the new word, and then writing back the rest of the file from the buffer. And for every IO operation, we have to check for an error:
sourceFile, err := os.OpenFile(file, os.O_RDWR, 0) if err != nil { return err } var info os.FileInfovar restFile []byteseekTo := offset + len(from) if info, err = sourceFile.Stat(); err != nil { goto closeAndReturn } restFile = make([]byte, int(info.Size())-seekTo) if _, err = sourceFile.Seek(int64(seekTo), 0); err != nil { goto closeAndReturn } if _, err = sourceFile.Read(restFile); err != nil { goto closeAndReturn } //...5 more calls to truncate file and replace string closeAndReturn: closeErr := sourceFile.Close() if closeErr != nil && err == nil { err = closeErr } return err
Conclusion
Playing with Go has been a really nice experience and a great opportunity to discover a new language. It’s really cool when you can code something different and leave your comfort zone. I’m looking forward to participating in more Go Challenges.
Go is a great language with a lot of cool features. When you start coding, it charms you immediately and you want to use it more and more. Moreover it’s ready for production. I’m looking forward to use it in future projects!
Michael Leroy
Posted at 22:16h, 21 SeptemberVery nice post, thank you! One quick question: how does the language enforce the contract imposed by implementing an interface if you don’t need to explicitly define that this contract exists using a “implements Foo”?
Dmitry Pokidov
Posted at 08:06h, 22 SeptemberIt’s checking implementations in compile time. It means that if you are not using your struct as implementation of the interface then you won’t get error. I copied an example with interface to Go Playground – http://play.golang.org/p/_40inD5AYx. You can see what will happen if you will change interface declaration.
Fernando Maquedano
Posted at 09:29h, 22 SeptemberGreat article. Thanks!
Dmitry Pokidov
Posted at 10:50h, 22 SeptemberThank you, mate!
Mr Magoo
Posted at 11:01h, 23 SeptemberStruggling with how you failed to choose one of “bike riding on the Great Ocean Road or having a garage party with friends”.
Good article.