Exploring Go

It is what we know already that prevents us from learning - Claude Bernard

Intro

I have been writing in Go for 6 months. For an experienced programmer, learning a new language is about quickly becoming fluent in syntax and the fundamentals of programs such as control structures, objects, data structures, variables, error handling etc. and then applying/comparing these basics to languages you already know. I am fluent in Ruby, Java and a bit of Javascript. However, sometimes this previous knowledge hinders my learning process. The main question for me in this phase is how easy it is to learn the language and how easy is to write idiomatically in the language.

Besides language fluency, programmers also need to work in a new environment with new tools. Understanding how dependencies are managed in the new language, what tools are available to build and test programs and becoming comfortable with these tools is also a requisite skill. Not only that, you need to know how the programs can be run in various platforms and what the differences are in building. Usually, thinking about running programs on the various OS’/platforms prompts me to research the design of the language runtime and the performance of the language when it comes to scaling and memory management.

Once I can get competence in writing, testing and running programs, I focus on my favorite, most fun part- which is to discover what’s different in the language. What are the features, paradigms that I cannot map to existing languages or at least not completely? This is also when I go back and revisit the origins or philosophy of the language, why the language was created and what problem domain is it best at solving.

Lastly, having played with it, I like to know what useful things I can build with it, which leads to, what libraries and frameworks are available to build practical applications.

This is a 4 series post. In part 1, I am covering my experiences and learnings with attaining some level of language fluency. But, first Why Learn Go, when there are so many languages to choose from.

Why Learn Go?

Keep calm and learn a new language

Performance:

Go is a typed language that is compiled to machine code. Unlike other new languages that have relied on LLVM such as Swift and Rust, Go has been built from the ground up. The language team is focused on continued improvement of performance, For example you can see from this slide the improvement that has been made to the garbage collector from 1.5 to 1.6, and this statement from the Go 1.6 release talk. “For Go 1.6, a lot of work went into optimising and tuning the gc, enabling far larger heaps to be managed within the 10ms latency goal.” Go has “goroutines”, that are much more efficient and cheaper to create than threads.

Simplicity:

Simplicity was a key motivation for the design and if you are serious about the language, then this paper by Rob Pike is a must read. A lot of that article is geared towards programmers who have been writing in C/C++ and are tired of dependency management and long compile times. In Go, compile times are fast and so the immediacy of running a program feels like a scripting language, which is another win for Go when it comes to performance. Also, it does not suffer from continuous language changes- in fact, a big pemise in Go is to have no change in the language. See 1.6 release notes. This is in fact quite lovely, especially if you have written any Javascript and have suffered from Javascript fatigue. The Go Standard library is very good. For example the net/http library is used directly to write servers and there is no need to use layers upon layer of frameworks to write, such as in Ruby or Javascript. Ruby ships with Webrick webserver which they recommend NOT to use, and so you end up spending a lot of time learning pros and cons of a lot of different webservers in Ruby before even shipping an app.

Concurrency

Go has built-in support for conncurency with Goroutines and Channels. This makes Go a natural choice for writing back-end systems with messaging, caching and scalability requirements. This is particularly attractive to programmers that are comming from the single threaded world of Ruby and have had to use frameworks such as eventmachine for implementing networked applications. This was a really great presentation by Ilya Grigorik at Mountain West Ruby Conf in 2011 in which he presents a framework for concurrency in Ruby. Reading the slides you can see how the framework matches the Go primitives for concurrency.

Deployment

In Go, dependencies are statically linked, so you have single binary to deploy. Also, read the answer in Go’s FAQ as to why is my trivial program such a alarge binary. It simplifies things greatly for deployment. You don’t have to worry if your users have Ruby or the JVM installed, and if so, what version.

How to Learn

For language fluency, the go tour was excellent, I have revisited it a few times since. The go tour runs in a sandboxed playground. The playground, is like an online code editor, similar to jsfiddle for writing small demo programs and sharing. The other useful tool I found was learningxiny, most for syntax reference and reading the effective go guide. Some people learnt simply by reading the golang specification. Initially, I found it a bit terse. For me it is more useful to reach for the specification as a reference when I get stuck on a concept and I have been doing that more and more as I have gained fluency in the language. Another helpful resource is the go wiki. Finally, the most useful learning tool for me was practicing exercises in exercism. Doing the exercises gave me the confidence to build, test and run small Go programs and also get feedback on whether I was coding idiomatically. As an aside, Go does not come with a REPL. If you are used to REPL’s and trying things out quickly on the command line this gets to be a little bit annoying. gore is a nice tool for this purpose. Tt’s useful for simple things, like evaluating expressions or understanding language internals but since the language is statically typed and history is maintained, you start seeing a lot of errors for long sessions. It is also not practical for testing anything complex like channels and goroutines.

A few language concepts

Structs & Functions

Go has no classes. It’s not really OOPS but not really functional. This is the feeling I get while writing Go, the syntax also felt a bit like Pascal, particularly the := operator. The := notation serves both as a declaration and initialization. Being able to do both of these in such a precise way I think is one of the many reasons the language feels like a scripting language. Language constructs seem straightforward at first, which is deceptive, since there are nuances. There is very little syntax sugar and only 25 keywords. It is a statically typed language so if you have been coding in Ruby or Javascript for a while, type checking takes some getting used to. You may get errors such as;

For example, in Ruby, this is legal:

1
2
3
4
irb(main):001:0> a=3
=> 3
irb(main):002:0> a="test"
=> "test"

But in Go, you will get an error since once a variable is declared with a type, it can only be assigned values of that type. This is more of just refreshing the concepts from Java, and typed languages, which I haven’t coded much in last few years.

1
2
3
4
gore> a:=3
3
gore> a:="test"
: cannot use "test" (type string) as type int in assignment

Struct is the closest thing to classes in Go. It is the thing in which you encapsulate data and behavior. Object oriented languages have a concept of “this” or “self” that implicitly refers to the current instance. Go doesn’t have this concept, but by adding functions where the receiver is a struct, you can add OOP like behavior to the struct.

Example:

type myStruct struct {x,y int} func (myStruct m) Run()....

The above can be invoked as myStruct.Run() which looks a lot like a class/method paradigm in OOP.

So how do you create new instances of a struct? There are several ways.

Declaring a var, without initializing creates a zero value struct. Read more on golang zero values here Go has zero values and even nil is a zero value. Read more at Understanding Nil

1
2
3
4
gore> type myStruct struct {x,y int}
gore> var d myStruct
gore> d
main.myStruct{x:0, y:0}

Initialization with literals. Go has a nice literal declaration, which has a json like feel to also create struct

1
2
gore> b:= myStruct{x:1,y:1}
main.myStruct{x:1, y:1}

Factory Functions are also common and functions with a NewMyStruct aka Newxxx pattern are used. It kind of feels like a constructor because of the naming with New**. Effective Go calls them initializing constructor

1
2
3
4
5
6
7
func NewMyStruct(x, y int) (myStruct, error) {
    if x >= 0 && y >= 0 {
        return myStruct{x: x, y: y}, nil
    }
    return myStruct{}, errors.New("x and y should be positive integers")
}
NewMyStruct(1, 2) // creates a struct myStruct with x=1 and y =2

From the above code you can see the support in go for “multiple returns”, “parallel assignments” and “error handling”.

Error handling

Error handling is an important feature of the language and handling errors early is expected in the flow of the code. If you read the code in the standard library, you will see that a lot of functions with multiple returns usually have the second parameter as the error.

1
2
3
4
5
  file, err := os.Open(path)
  if err != nil {
     // handle error
  }
  defer file.Close() // defer closing the file till the surrounding function returns

You can also inline errors with an if initializing statement. This is a nice shorthand when only dealing with the err value.

1
2
3
if _, err := f.Read(file); err != nil {
      //handle err
}

At this point, I was wondering, does go not have primitives equivalent to try/catch block? and what is “defer”, is it kind of like “finally”? The golang blog has a nice post that covers these concepts defer/panic/recover. Defer is simply a list of functions that get executed after the call returns. Panic’s are runtime errors and recover() is a function that a panicking goroutine can recover from. panic/recover is a bit like throw/catch in Java. I typically use panic statements to mark my unimplemented code, func NewAPI() { panic("not implemented")}. Note that panic and defer are functions, error is a struct, but defer is a statement, it’s a language primitive.

Higher Order functions

Go has higher order functions. I have worked with higher order functions in JavaScript and Ruby’s Enumerable api is so so lovely. Writing functional code in go, is meh. It feels javascripty, except for this one nice thing. The function’s returned from functions have types. This is nice to have, especially if you have written loads of Javascript and have memories of debugging such issues. This is one part where I really miss ruby.

An example of higher order function. Compute sum of n numbers.

1
2
3
4
5
6
7
8
9
10
11
12
func SumOfN(numbers ...int) func(limit int) int {
    return func(limit int) int {
        sum := 0
        for n, i := range numbers {
            if n >= limit {
                break
            }
            sum = sum + i
        }
        return sum
    }
}

To invoke the above call SumOfN(1, 2, 3, 3, 3)(2) # Returns 3 SumOfN(1, 2, 3, 3, 3)(3) # Returns 6 Try it in playground: https://play.golang.org/p/vxHy0SxaD7

Control structures

Control structures are very simple to remember. There are Loops, Switch statements and if/else. That’s it. Oh, and there is no ternary operator. Every developer I met seems to look for it when the start with Go. Also, there is only one kind of loop, the for loop and you use it for everything. After writing in Ruby, I am glad to write in a language that doesn’t disown the for loop. For loops, for me, is still the most intuitive way to write algorithms.

1
2
3
4
5
6
7
8
9
10
11
12
13
// for loop with a condition, usually a while loop in other languages
for condition {
    // body
}
// the standard index for loop
for i := 0; i < count; i++ {
    // body
}

// range loop
for ... := range ... {
   // body
}

Range loops can be used over collection, which in golang is slices/arrays/maps, also strings, similiar to other languages. However, in Go they are also used to read values from a channels.

It’s possible to range over a string, but the values it gives you are rune. The word rune was a bit confusing for me as I expected this to be a “char”. The Go blog post addresses this confusion by stating that “They find the idea of "character” is a little hard to define and therefore The Go language defines the word rune as an alias for the type int32, so programs can be clear when an integer value represents a code point.“

Example: https://play.golang.org/p/RLC1-5B1kg Note the multibyte characters in the string, go allows you to range over them, They are not carachters, but runes, see post here

Arrays, Slices, Maps

In Go, the primitive collections—array, map, and slice are strongly typed. Slices are sort of like dynamic arrays and a very handy data structure.

Go does not have generics so you cannot have operation that works with multiple types, such as print a list of items in an array, you cannot do that with a generic function for example. To further clarify, in Java I was used to writing something like the code below. You did not need to have separate print statements for each array type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// generic method printArray                         
public static < E > void printArray( E[] inputArray )
{
    // Display array elements              
     for ( E element : inputArray ){        
        System.out.printf( "%s ", element );
     }
     System.out.println();
}
public static void main( String args[] )
{
    // Create arrays of Integer, and Character
    Integer[] intArray = { 1, 2, 3, 4, 5 };
    Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
    printArray( intArray  ); // pass an Integer array
    printArray( charArray  ); // pass an Integer array

Someone on golang-nuts forum asked this question on this thread. `Is it possible to express "any map”, “any array” or “any slice” in a Go type switch?, and the answer from Rob Pike was “No. The static types must be exact.” From the Go slices blog, I quote, “A weakness of Go is that any generic-type operations must be provided by the run-time”. Go provides a built-in generic append/copy/delete function, just for this purpose. It works the same for any slice type.

1
2
3
4
5
6
7
8
9
10
gore> a:=[]int{1}
[]int{1}
gore> a=append(a,2) 
[]int{1, 2}
gore> a=append([]int{3},a...) // Prepend also uses append
[]int{3, 1, 2}
gore> d:=[]string{"one"}
[]string{"one"}
gore> d=append(d,"two")
[]string{"one", "two"}

Type Conversions, Interfaces, Reflection are other big topics, which I hope to cover in future blog posts. In Part II, I will cover the language environment and tools.

References:

  1. Why I Like Go
  2. The Go Programming Language
  3. Ardan labs Ultimate Go
  4. How Go Routines work
  5. The Little Go book