Go for Rubyists, II

Share this article

go

In this article, we’ll be looking through some more of Go’s features through a Ruby lens. In case you missed it, you can find Part I here, although you don’t have to read it to understand this article.

We’ll be running through Go’s answer to traditional OOP strategies (e.g. inheritance). Go essentially eliminates traditional inheritance to provide a more fluid object structure. Then, we’ll take a look at a Go web framework called Web.go.

Let’s jump in.

The Fallacy of Inheritance

Especially in non-trivial projects, inheritance is pretty common practice. But, inheritance can complicate the implementation of even simple changes (which, as we all know, are bound to occur) and also create a need for duplication of code.

For example, if you have a class called “Horse” and two subclasses “GallopingHorse” and “SadHorse” (maybe these two have totally different characteristics; not just a state change). What if you have a horse that is sad and galloping? That would mean some of the behavior is locked up within each of these classes.

There are also other issues with inheritance. It inherently makes your code rigid since it involves relationships between types. As Golang’s FAQ notes:

“Object-oriented programming, at least in the best-known languages, involves too much discussion of the relationships between types, relationships that often could be derived automatically. Go takes a different approach.”

So, what does Go do instead?

It uses interfaces. Ruby doesn’t have a concept of interfaces baked in, but you can get them to work. Essentially, an interface is like a contract. If a class called “Horse” “implements” the interface called “Animal”, the Animal interface says that it must have the methods “eat()” and “sleep()”. What’s the point, you ask? Well, let’s break out a bit of pseudo-Ruby:

#this is Pseudo-Ruby. We're pretending that Animal is an "interface"
#and Horse is a class. 

def full_day(animal)
  animal.eat()
  animal.sleep()
end

First of all, we’re pretending for the moment that Ruby would refuse to run this code if the argument to the “full_day” method did not implement the “Animal” interface. So, we can have a method that only expects something that satisfies the “Animal” interface; it doesn’t particuarly care whether or not it is a “Horse”, “Duck” or “Person”. Since the real Ruby is a dynamically typed language, interfaces are differently applicable; the code would compile to bytecode correctly regardless of whether or not “animal” had the methods “eat”, “sleep”. However, in Go, a statically typed language, the interface would be verified during compile time.

Enough theory. Let’s build a few examples.

Animals

First of all, we need some kind of object. Let’s make ourselves a “Horse”:

package main

type Horse struct {
  name string
}

func main() {

}

The line that says type Horse struct basically says “I’m making a new type, call it Horse and it should be this kind of struct“. A struct or “structure” is essentially a collection of data structures (e.g. strings, ints, arrays, etc.) We have structs in Ruby that are very similar, except that they give you getter and setter methods for free. Considering the struct, it notes that the struct has one piece of information called the “name” of the Horse (notice that the type of the data comes after the name of the data in Go).

Now, let’s define a method or two on our Horse:

package main

import (
  "fmt"
)

type Horse struct {
  name string
}

/* added a couple of methods here */
func (h *Horse) Sleep() {
  fmt.Println("zzz...") 
}

func (h *Horse) Eat() {
  fmt.Println("omnomnomnom")
}

func main() {

}

Notice how we’ve defined the methods. They are declared completely outside of the struct, as opposed to Ruby, where the methods are declared in the class declaration block. Also, they have a (h *Horse) in front of them. This tells the compiler that these are methods that can be called on the Horse class and also provides h, an instance of Horse, to the given method.

Let’s fix up the Animal interface:

package main

import (
  "fmt"
)

/* added an interface */
type Animal interface {
  Eat()
  Sleep()
}

type Horse struct {
  name string
}

func (h *Horse) Sleep() {
  fmt.Println("zzz...") 
}

func (h *Horse) Eat() {
  fmt.Println("omnomnomnom")
}

func main() {

}

It is only a couple lines of code, but it describes the behavior of all Animals: they eat and sleep.

Interfaces in Go are not explicitly implemented. If you’ve written any Java code before, you’ve likely seen something like this:

class Horse implements Animal

In Go, as long as the required methods have been written, the interface is automatically implemented. So, our Horse object already satisfies the Animal interface! Before we go any further, let’s create an instance of this Horse called George:

package main

import (
  "fmt"
)

type Animal interface {
  Eat()
  Sleep()
}

type Horse struct {
  //changed the name of the field from
  //"name" to Name
  //fields that begin with an uppercased letter
  //are public fields.
  Name string
}

func (h *Horse) Sleep() {
  fmt.Println("zzz...") 
}

func (h *Horse) Eat() {
  fmt.Println("omnomnomnom")
}

func main() {
  /* create an instance */
  george := new(Horse)
  george.Name = "George"

  george.Sleep()
  george.Eat()
}

There are a couple of things to notice here. First of all, I changed the field called name to Name. Fields starting with an uppercase letter are public. Considering the main method, I used the new builtin to create a reference to a Horse. Finally, we name it and call the Eat and Sleep methods on it. Let’s make this a bit more interesting; how about a method that should work for all animals:

func FullDay(a Animal) {
  a.Eat()
  a.Sleep()
}

If we pass in George with a FullDay(george) call, we should have the same output as before. Out of all of this, we can see how interfaces work in Go. You define a data structure and attach some methods to it and the interfaces it fullfills will be automatically determined. This serves to make your codebase more flexible and easy to change if need be.

Web.go

Now that we have a good hold on how abstraction works in Go, we can take a look at Web.go, an awesome web framework for Go.

First of all, you’ll need to have a working “Go environment” in order to run these on your system. In order to do that, I would encourage you to clone the example repository. Then, once you are in the “webgo-examples/” directory, you can run this in your shell:

export GOPATH=`pwd`
cd src
go get github.com/hoisie/web

That should get you a copy of the Web.go framework. If you crack open the “main/hello-world.go” file, you should see this:

package main

import (
    "github.com/hoisie/web"
)

func hello(val string) string { 
    return "hello " + val 
} 

func main() {
    web.Get("/(.*)", hello)
    web.Run("0.0.0.0:9999")
}

This is the simplest possible use case for Web.go; a tiny “hello” application. As you can see, Web.go is very similar to Ruby’s Sinatra; one file with routes and corresponding methods. However, the routes in Web.go are specified via regular expressions. As a personal preference, I find Sinatra’s way of doing things much more direct and less error-prone, but there are many who prefer regular expressions for their power.

If you build and run “hello-world.go” with a “go build hello-world.go; ./hello_world”, you should see a server running. Navigating to “localhost:9999” should return a “hello” message. We can write this same example a slightly different way (borrowed from the Web.go docs):

package main

import (
    "github.com/hoisie/web"
)

func hello(ctx *web.Context, val string) { 
    ctx.WriteString("hello " + val)
} 

func main() {
    web.Get("/(.*)", hello)
    web.Run("0.0.0.0:9999")
}

Notice that the parameters to the hello method have changed; we now have a context method passed in to the method. This context object is pretty useful:

package main

import (
    "github.com/hoisie/web"

    "fmt"
)

func logName(ctx *web.Context, val string) { 
  name := ctx.Params["name"]
  fmt.Println(name)
  ctx.WriteString("Hi, " + name)
} 

func main() {
    web.Get("/log_name?(.*)", logName)
    web.Run("0.0.0.0:9999")
}

As you can see, we’ve wired up a route /log_name that reads a parameter called name, responds to the request and prints out the name. If you head over to /log_name?name=Whateveryournameis, you should get a friendly greeting.

Web.go has nice support for static files. If you add a folder named static to the directory of the web app, Web.go will automatically serve requests to matching filenames, (e.g. “/css/index.css” corresponds to “static/css/index.css”).

There’s also a pretty cool set of tools that lets us build a streaming response. An example is included with the Web.go documentation, but it doesn’t have much of an explanation:

package main

import (
    "github.com/hoisie/web"
    "net/http"
    "strconv"
    "time"
)

func hello(ctx *web.Context, num string) {
    flusher, _ := ctx.ResponseWriter.(http.Flusher)
    flusher.Flush()
    n, _ := strconv.ParseInt(num, 10, 64)
    for i := int64(0); i < n; i++ {
        ctx.WriteString("<br>hello world</br>")
        flusher.Flush()
        time.Sleep(1e9)
    }
}

func main() {
    web.Get("/([0-9]+)", hello)
    web.Run("0.0.0.0:9999")
}

If you run this and go to “/10”, you get a response that keeps adding more “hello world”s to the page. How does this work? Essentially, insteading of sending back a full response to the client (i.e. the web browser), the server is sending it parts of it. If you take a look at the code, the magic happens with the “flusher” object. We use it in order to “flush” some of our data down to the client then we hang around for a bit (with the time.Sleep) and send another flush and so on. So, we’re using the flusher to stagger the response and send it out in pieces. Of course, this means that the server can take lots of seconds per request, but in some (admittedly, they are few and far between; these days, WebSockets and Pusher can make this sort of thing happen) cases, this can be a pretty useful trick!

Even though Web.go is pretty fantastic, I don’t think it matches the simplicity of Sinatra; the latter just feels quicker and more nimble (so does Ruby, in general). I think a large portion of it may be attributed to the “write-compile-run” cycle with Go that is nonexistent in interpreted languages. However, even in terms of the code itself, Go seems more longwinded than Ruby and its cousins.

Web.go does demonstrate the versatility of Go. It can be used as a systems language (I am currently using it for work with distributed filesystems) and also reasonably well for web development, not to mention the performance boost.

Conclusion

Go is a pretty awesome language. In my opinion, it manages to retain some of the best features of dynamic languages while staying true to its systems background.

I am currently working on a Go-based project dealing with Hadoop. I can say, without hesitation, my experiences with Go have been fantastic in this regard. As to web development, I have yet to deploy a major application with Go, but the overall feeling has been nice. As I mentioned, the Web.go is not as quick as Sinatra, but some of the features it offers carve it a space in my toolbox.

Dhaivat PandyaDhaivat Pandya
View Author

I'm a developer, math enthusiast and student.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week