Ready, Steady, Golang

 

Ready, Steady, Golang

So apparently learning new languages is good for you. So I’ve spent the last week getting familiar with Go.

Here’s what this post isn’t:

This post isn’t meant to be a series of exercises to teach you Go, if that’s what you’re after, I’d like to recommend the official tutorial - Tour of Go. It’s not even an organised way to expose you to the basics of Go, for a structured approach to explaining it check out Learn X in Y minutes.

It isn’t a post about Go through the years, a good retrospective blog post about Go in the past years can be found here. It will not talk about what Go is good for, there is an informative post covering exactly that. Neither it is a post about when not to use Go, as you would expect, I’ve got a link to one of those too.

Here’s what this post is:

This post is essentially me listing things I found curious about Go. If you’re contemplating checking it out - perhaps these will be enough to pique your interest.

Briefly About Go

Go or golang was created at Google in 2007. It is compiled, statically typed. It’s free, it’s open source. Releases for it happen regularly (at the time of writing it’s on version 1.7.1). My first impression was that it kind of resembles both Python and C, but that’s coming from someone who has very basic experience with both.

With me coming from Java, Go looked a tiny bit strange - no semicolons at the end of lines; fewer commas than I would expect; functions, methods, interfaces… Let’s look at stuff:

Imports

You can import a single package like this:

	import "package1"

You can import several packages like this:

	import "package1"
	import "package2"

Or you can import several packages like this:

	import (
		"package1"
		"package2"
	)

Variables

Variable declarations look different to Java, possibly more readable. There’s definitely something about the flow of syntax in Go that I like.

	var value1 int

Or, if you’re initialising the variable:

	var value1 = 1

Alternatively (if you’re inside a function) - you can do the Python thing:

	value1 := 1

The joys of instantiation of multiple things are also present:

	value1, value2, value3, value4 := 1, 1.5, true, "text"

In which case we’ll end up with an integer, a float, a boolean and a string. If we do declare variables but don’t initialise them - they get set to default values (0, false, ““).

As a Java developer I’m used to things being public, protected or private. Go decides not to use additional keywords for that. If a value name starts with an upper-case letter - it can be exported from the package, if it starts with a lower-case letter - it cannot, and will be inaccessible outside the package.

	var ExportedValue string
	var unexportedValue string

Functions

Now functions are really cool, they’re easily my favourite part of the language. Like you would expect, they: can take in parameters, can not take in parameters, can return things, can not return things. Here is a function that takes nothing in and returns nothing:

	func printSomething() {
		fmt.Println("Ready, Steady, Golang")
	}

Here is a function that takes in a parameter and returns a bunch of things (oh, did I mention? functions can return multiple things):

	func printSomething(stringNumber int) (string, int) {
		if stringNumber == 1 { 
			return "Ready, Steady, Golang", 1
		}
		
		return "nope", 13
	}

Another interesting thing, you can name your return values and have a naked return. The function behaves like the return values have been defined at the top of it and everything works out splendidly:

	func printSomething(stringNumber int) (outString string, outInt int) {
		if stringNumber == 1 { 
			outString = "Ready, Steady, Golang" 
			outInt = 1
		} 
	
		return
	}

If stringNumber is not 1 the function will return the default values for those variables - an empty string and a zero.

Methods

Methods are a special kind of functions that have receivers. Here’s one:

	func (aThing Thing) doStuff() string {
		return "Thing's contents: " + aThing.contents
	}

Receivers are a kind of a contract that this specific method will only be used with a Thing. Golang doesn’t have classes, so this is a neat way to have type-specific functions. However, methods can only receive types defined in the current file. Here’s what the whole file would look like for my example:

	package main

	import (
		"fmt"
	)

	type Thing struct {
		contents string
	}
	
	func main() {
		thing1 := Thing{"boop"}
		fmt.Println(thing1.doStuff())
	}

	func (aThing Thing) doStuff() string {
		return "Thing's contents: " + aThing.contents
	}

If we wanted the same functionality without using a receiver it would’ve looked like this (note the change in main):

	package main

	import (
		"fmt"
	)

	func main() {
		thing1 := Thing{"boop"}
		fmt.Println(doStuff(thing1))
	}

	type Thing struct {
		contents string
	}

	func doStuff(aThing Thing) string {
		return "Thing's contents: " + aThing.contents
	}

Interfaces

Interfaces are weird. Interface types contain zero or more method signatures, but that’s not the weird part. The curious thing is that nowhere do you explicitly declare that something implements an interface. Allow me to demonstrate:

	package main

	import (
		"fmt"
	)

	type Booper interface {
		Boop() string
	}

	type AleksString string
	type AleksNumber int

	func main() {
		var booper Booper
		aString := AleksString("boop")
		booper = aString
		
		// aNumber := AleksNumber(23)
		// booper = aNumber
		
		fmt.Println(booper.Boop())
	}

	func (aString AleksString) Boop() string {
		return string(aString)
	}

	func (aNumber AleksNumber) Boop() string {
		return fmt.Sprintf("%v", aNumber)
	}

The commented out lines are there to show that it would work equally well with the other type.

You can also use interfaces with no specified methods (empty interfaces). These can hold values of any type. A nifty way of not specifying all the possible potential value types.

Other Cool Things

Go makes use of pointers and the suggested good practice is to use pointers as function arguments and in receivers. I think the main reason is performance, since the variable no longer needs to be created and destroyed specifically for the function. Pointers provide a way to modify the value of a variable directly in the function, without returning and re-assigning.

Struct types in Go are collections of fields, which is fine, but not neccessarily cool. However, here are two valid ways to instantiate them:

	type Superhero struct {
		name, costume string
	}
	
	func main() {
		batman := Superhero{"Bruce Wayne", "bat suit"}
		nudeman := Superhero{name: "John Doe"}
		fmt.Println(batman, nudeman)
	}

Nudeman (totally a Marvel superhero, see him in the next Avengers) gets created with a name of “John Doe” and an empty string for a costume. Sort of cool, right?

Easily one of my favourite aspects of golang are compilation errors for unused variables. If that won’t teach people to create only the variables they work with - I don’t know what will.

I’ve been told that a great way to finish a blog post (or any piece of writing, really) is to provide closure.