Go for Rubyists

Note: Here’s another guest post from our friends at the Hybrid Group.

So you’re a Ruby developer and you’ve heard about this cool new language called Go, but you don’t know where to get started. You’ve read the bullet points and got a litle scared. Static typing? Compiling? Is this the 80’s all over again?! Well sorta! Go gives us the power of concurrent operations with a built in GC, so we get the power of a compiled language, but the lazyness of a dynamic language. Interested yet? Let’s get into the basics of the language and cut out all the uncessary exposition and look at some code.

#Hello world Here’s the canonical “Hello world” in Go.

package main

import "fmt"

func main(){
  fmt.Println("Hello world!")
}

And its ruby equivalent

puts "Hello world!"

At first glance you may think “Wow that’s verbose!”, but if you have a c/c++/java background, this doesn’t look too out of the ordinary. Now let’s discect this program and firgure out what’s going on.

The package keyword identifies the scope of the code you’re writing to the Go environment. Since we’re not writing a library, our package has to be main, so that Go can execute this code when you run your generated executable.

The import function pulls in external packages to be used in your program. The fmt package is the standard package used for formatted IO operations.

Now we get to the main() function. If you’ve written a C program before, you know you’ll need a main() function as the initial entry point for your programs execution. The main() function in Go serves the same purpose. If you’re not writing a library for later use, you need a main() function in order for your Go program to actually run and do something.

Finally we get to the point where are program actually does something! The line fmt.Println("Hello world!") calls the fmt package’s Println function and prints Hello world! to the console. Calling an external package’s functions follows the same format, package_name.function_name.

That’s all there is to a Go program!

Variables

Variable definitions in Go are pretty much like every other programming language ever, with the exception that they come in two flavors.

There’s the implicit type instantiation

s := "Hello World!"

This creates a variable s as a type string and gives it the value Hello World!. This is straightforward, but what if you want to create a variable for later use? Go has another way of defining variables.

var arr []int

for n := 0; n < 5; n++ {
  arr = append(arr, n)
}

fmt.Println(arr)

This will create an int array which is scoped outside of the for loop, so any changes to arr within the for loop persist in the scope of the function in which the for loop resides.

The line arr = append(arr, n) is calling the builtin append function which takes an array, arr, and appends n to the provided array. It then returns a new array with contents of arr with n pushed on at the end.

Easy stuff, right?

Control Structures

Go only has 3 control structures, for, if and switch. Wait a minute, only 3? Yes!

The if statement is your standard fare

if true {
  fmt.Prinln("I'm true")
} else {
  fmt.Println("I'm false :(")
}

The for statement on the other hand is much more interesting. Instead of having all sorts of while, do, loop and each loops, Go has one for loop which allows a few different forms.

The first form is your standard “iterate until false”

for i := 0; i < 5; i++ {
  fmt.Println(i)
}

Another form is sorta like a while loop

b := true
i := 0
for b == true {
  fmt.Println("doin some stuff")
  i++
  if i > 1 {
    b = false
  }
}

And the last form is like an each_with_index do sorta thing

arr := []string{"a", "b", "c", "d", "e"}
for index, value := range arr {
  fmt.Println(index, value)
}

which is ruby would be

["a", "b", "c", "d", "e"].each_with_index do |value, index|
  puts "#{index} #{value}"
end

And last, there’s the switch statement which is pretty much exactly how you would imagine it to be

i := 1
switch i {
  case 1:
    fmt.Println("its a 1!")
  case 2:
    fmt.Println("its a 2!")
}

There are more variations and nuances to these control structures, so be sure to consult the Go documentation on their specifics.

Functions

Functions in Go are first class. A first class function is a function which you can define and pass to another function for execution at a later time.

Let’s look at a function definition.

func MyFunc(a string, b int) bool

If you come from the c/c++/java world, this function defition may look backwards to you. The variables types are defined after the variable name with the return type at the end of the function signature. So this function accepts two parameters, one string one int and returns a bool.

Now what about closures? Go has got you covered there!

func (){
  fmt.Println("some function stuff")
}()

Notice the extra () at the end of the function definition, that means that the function should execute immediately. Now what if you wan to pass a function and execute it at a latter time. Won’t that be interesting!

func ExecuteMyFunction(f func()) {
  f() 
}

func main {
  f := func() {
    fmt.Println("some function stuff")
  }
  ExecuteMyFunction(f) 
}

In the above example I define a function and assign it to the variable f. I then pass it to the function ExecuteMyFunction which expects a variable of type func() and then using the () on my variable, it is now executed.

Structs

Go isn’t an object oriented language. Instead they have the concept a struct and structs have functions attached to said struct. You then create an instance of that struct and then have access to that structs functions and variables. Here’s a simple Go program with an equivalent ruby example.

package main

import "fmt"

type Beer struct {
  Brewery string
  Name string
  Style string
}

func (b Beer) IsTasty() bool {
  if b.Style == "Light" {
    return false
  } else {
    return true
  }
}

func (b Beer) ToString() string {
  return fmt.Sprintf("%v %v", b.Brewery, b.Name)
}

func main(){
  b := Beer{"Spaten", "Optimator", "Doppelbock"}  
  if b.IsTasty() {
    fmt.Println("I'll take a", b.ToString())
  } else {
    fmt.Println("I guess I'll have water....")
  }
}
class Beer
  def initialize(brewery, name, style)
    @brewery = brewery
    @name = name
    @style = style
  end

  def tasty?
    if @style == "Light"
      false
    else
      true
    end
  end

  def to_s
    "#{@brewery} #{@name}"
  end
end

b = Beer.new("Spaten", "Optimator", "Doppelbock")
if b.tasty?
  puts "I'll take a #{b.to_s}"
else
  puts "I guess I'll have water...."
end

In the Example above I define a struct with the syntax type Beer struct. This creates a struct of type Beer. Congratulations, you’ve just created a new type in Go! A struct definition has a list of variable names with their types, and to assign function to a struct you simple add a (b Beer) in between the func keyword and the name of the function. Think of the b in (b Beer) as the self keyword. Any function with the (b Beer) is only accessible to an instance of the Beer struct.

You may be thinking “What about private methods and variables?” You’re absolutely right! The way Go defines private functions and variables is quite easy. All you need is a lower case letter at the beginning of the variable/function name and it’s now private!

type Person struct {
  Name string
  DOB string
  ssn string
}

func (p Person) GetSSN() string {
  return p.superSecretFunction()
}

func (p Person) superSecretFunction() string {
  return p.ssn
}

As you can see in the example above the Person struct has the private variable ssn as well as the private function superSecretFunction. Both ssn and superSecretFunction are only to available the Person struct itself, so the user has no direct access to that information. Neat!

Enter the goroutine

Now we’re getting into the real power of Go. If you’ve tried to program concurrently in ruby, you have undoubtedly lost a few hairs. There are good libraries such as Celluloid for concurrent programming, but wouldn’t it be awesome if there were language primitives which did it all for you? Luckily Go has got you covered!

Go makes it so easy to do a concurrent operation, all it requires is the go keyword in front of your function call, and you’re done. Super easy.

go ReallyLongAndExpensiveOperation()

Sure that looks easy enough, but how do you share data between goroutines in a safe way? Channles my friend, it’s all about goroutines and channels. Channels comes in two flavors, unbuffered and buffered. Unbuffered channels will block on writing to the channel, until the channel has been cleared. Whereas a buffered channel will queue up channel messages until you hit your defined buffer size, then it will block.

In this example I will call a function which which randomly sleeps for a duration, but I want to know how long my function slept for. Using an unbuffered channel, I can retrieve those sleep times just after they happen.

package main

import (
  "fmt"
  "time"
  "math/rand"
)

func ReallyLongAndExpensiveOperation(channel chan int){
  for {
    rand.Seed(time.Now().UTC().UnixNano())
    i := rand.Intn(1000)
    time.Sleep( time.Duration(i) * time.Millisecond)
    channel <- i
  } 
}

func main() {
  channel := make(chan int)
  go ReallyLongAndExpensiveOperation(channel)
  for {
    fmt.Println("I waited", <-channel, "milliseconds")
  }
}

Wrap up

As you can see Go is a power language, especially for concurrent operations. If this quick overview has wet your appetite, you should head over to the tour of go which will go into more detail about the concepts I’ve outlined here.

Oh and do yourself a favor and run go fmt on your project before you commit it to github. Go has fantastic tools to format your code with the Go standard indentation and white space practices.