Go! Speed Racer Go!

I finally reached a point where I could start running the go version of sm-photo-tool. I finished the option validation for the list command. While I was testing it I noticed how much faster the Go version felt. Here are the python vs Go versions of the commands.

The python version took 80 milliseconds to run the validation of parameters and print out the error message. This is expected, you have to start the python VM (and if not precompiled, the code would need to be compiled). In this test, the code was precompiled.

$ time sm-photo-tool list invalid blah
ERROR: valid options are ['album', 'galleries']

real	0m0.080s
user	0m0.059s
sys	0m0.021s

Ok so how fast is the Go version? My guess was half the time, 40ms. I was way off. Try 6ms. SIX! That’s amazing to do the exact same amount of work.

$ time ./sm-photo-tool list invalid blah
ERROR: valid options are [album galleries]

real	0m0.006s
user	0m0.001s
sys	0m0.005s

Control your caps

On all of my computers I use gnome-tweak-tool to remap CAPSLOCK to CTRL. But there are times when I’m working in a virtual machine that doesn’t have that setup so I need to go back to old school methods.

If you’re on a box just run this from any terminal:

setxkbmap -option ctrl:nocaps

That avoids trying to kill a program with C which usually doesn’t work 🙂

Private Golang, reporting for duty

Many modern programming languages have a mechanism for controlling the visibility of members and methods. Most of the languages use keywords like private and public.

Java and C++ use public, private, and protected. Ruby also uses keywords: private, and protected.

Python on the other hand uses convention to control visibility, prefixing your methods with _ or __. Even then it’s more of a suggestion in Python as you can call it anyway. There is nothing to prevent it from actually being called from other code. It’s an understanding among Python developers that if there is an underscore you shouldn’t call it.

Go also has no keyword to control visibility. What no keyword? There has to be something? Maybe it uses _ like python? Nope that won’t do anything. Then what would Go use to control visibility? Go uses a very simple syntax, capitalization.

CAPITALIZATION? What? Yes, that’s correct. A simple capital letter makes the member or method public. Lowercase member, methods are private.

// GetName is a public method of User struct
func (u *User) GetName() string {
  return u.name
}

// nickName is a private method of User struct
func (u *User) nickName() {
  // do something
}

The same logic applies to members. Given the following example struct, Foo is public and bar is private:

type Example struct {
  Foo string
  bar string
}

So if you want to make your Go methods or members private, just start them with a lowercase letter. Want others to use it, capitalize it.

Constructors? Not in Go

If you’ve used any object-oriented language in the past decade, C++, Java, Python, Ruby, C#, you are used to defining a class and each having its own constructor. Here’s a sample in Java and Python, respectively.

public class User {
    private String username;

    public User(String name) {
        username = name
    }
    // ...
}
class User(object):
    def __init__(self, name):
        self.username = name

Previously, I wrote about Go having no class. Go uses structs. I covered how to write a struct and add methods to it. But now how do you create one? Let’s continue with the user example from above. In Go, we’d create a user struct:

type User struct {
    username string
}

Ok that was easy. But what about the constructor? Go doesn’t use constructors as you are accustomed. Go will basically initialize everything to their ZERO value, i.e. ints are 0, strings are empty. structs will have their members set to their respective zero values. Let’s look at the User example above.

u := User{}

This creates the User u instance with the username set to the zero value for strings, “”.

Sometimes the zero value isn’t enough. Sometimes you want to pass in values you know you need for that particular instance. That’s pretty simple too, again let’s look at the User struct:

u : User{"joeuser"}

This will set the username to joeuser. What if the struct had more attributes? You can either specify all the values in order or by name and value. Let’s add the age attribute to the User struct.

type User struct {
    username string
    age int
}

Initialize both by passing all the values:

u := User{"joeuser", 30}

Alternatively, we can initialize the username and leave the age to be it’s default zero value.

u := User{username: "joeuser"}

But what about something more complex than a two field User object? Since Go doesn’t have constructors, you have to create what I like call a “pseudo constructor”, basically a method that returns the initialized struct.

func NewUser(name string, age int) *User {
    // do whatever work you need to do
    // calculate fields, set defaults, etc.
    if age < 18 {
        fmt.Println("user is a minor")
    }
    return &User{name, age}
}

The name can be anything, I like to use NewStructName as a convention to make it easier to see what they are for.

While Go doesn’t have constructors, there are still three ways you can “construct” objects:

  • using the zero values
  • using the {} to pass in the values to the struct
  • creating a construction function to build the struct

Now “Go” and construct some objects.

Golang: search an array

Go is a pretty awesome language, typed, C-like, fast. I’m porting one of my python applications to Go. Searching an array isn’t as simple as it is in python though.

In Python you can use in or not in:

if self.args[1] not in self.valid_options:
    print("ERROR: valid options are %s" % self.valid_options)
    sys.exit(1)

Go doesn’t have membership operators, so you have to search the string for what you want. Then you get the index and see if it actually exists:

i := sort.SearchStrings(lc.valid_options, args[1])
if i >= len(lc.valid_options) ||
  i < len(lc.valid_options) && lc.valid_options[i] != args[1] {

  fmt.Printf("ERROR: valid options are %v\n", lc.valid_options)
  os.Exit(1)
}