I’ve started learning Go in my spare time. The more I use it the better it gets.
It was a slow start though, at first I was amazed and the size of the binaries produced (but of course they are statically linked so include everything they need).
Then I struggled with the packging, and how to organise my own code to make bigger projects workable. This turned out to be a non-issue, but only after some experiementation.
I also struggled a bit with the sheer simplicity of the goroutine model, and using blocking on goroutines to create (and my experience with javascript and Node.js comes out here…) almost an event-driven program. It is complex at first, but then it just clicks and starts to make sense.
So, I have been enjoying it, and thought I would share some of the things that make it such a joy to work with.
After seeing this in Go, I wonder why so many languages don’t have it. I have a fair bit of exeprience with javascript in both browser and server environments and you can almost fudge this with javascript using continuation functions – but it’s not the same.
So what is this and how is it useful? Well consider javascript and the method JSON.parse
.
If you have a string which you think is JSON encoded and you want to decode it you would have to do seomthing like this:
var o, success = true;
try {
o = JSON.parse(input_string);
}catch(e){
//error occurred
success = false
}
if(success){
//all good
}
You use a try/catch block - which everyone knows are expensive - then catch the error
and handle it. Also, unless you return
in your catch
block you will still need to
use a marker variable to indicate success of the operation. You cannot put your success
code in the try/catch block, as you may mask exceptions that shouldn’t have been
caught at that stage.
That’s fine and it works great. You can’t return the error, as almost
any return value could have come from the input_string
, e.g. true
, false
, null
,
undefined
, etc.
With multiple return values we still have to handle the error, but we lose the try/catch. For a moment imagine we could do this in javascript, and we use an array syntax for the receiver:
var [o, e] = JSON.parse(input_string);
if( e ){
//error occurred
}else{
//all good.
}
Well, instead of a try/catch we have an if/else, but also we don’t have to worry about whether the operation succeeded or not as that is handled for us. This pattern is used a lot by asynchronous libraries for callbacks. The usual signature of a function taken for a callback is:
function (err, ... ){ }
So we pass in an error if it occurred, otherwise the rest of the arguments are the actual return value(s) – note that in this case we, again, almost get multiple return values (this is a case of the continuation function).
This may not seem like a big deal, but it’s an incredibly succint syntax.
The other major side to this is that it is assignment not just functions that can have multiple return values. They just have to add up on each side. For example, swapping 2 integers in javascript we would do:
var a = 1, b = 2, c;
a = c, b = a, c = a;
In Go we can assign two values in one statement so we can write:
a := 1
b := 2
a , b = b, a
No intermediary value. That’s pretty sweet.
We define the type of return values from functions in Go - as it is statically typed and as a bonus we can name the return variables in the function declaration, and omit them from the return statement.
func demo() (x int){
x := 10
return // x is returned anyway, even though we'd didn't say.
}
For more non-trivial examples, being able to traverse some complex if/else, switch,
or select statements then just being able to call return
and the assigned values
are returned is very useful. Of course you can also prevent this behaviour by explicitly
returning whatever you want.
Next time, the defer
keyword and channels and goroutines
…