Go revelations

Hoo boy.  There are some things which need to percolate in my subconscious for a while before I figure them out; when my brain accomplishes this feat, it’s a thoroughly enjoyable revelation.  When I was 20, it would happen in hours, or overnight; now it seems to take a bit longer (a couple of days), but I still enjoy it just as much.

Go has a sort of multiple inheritance based on a rule called shallowest-first.  Basically, it means that the compiler determines which function to use based on how many type indirections it has to go through to get to a function.  For example:

type F struct { }
func (f F) foo() {}

Here, f.foo() has a depth of 1 (you only have to dig down one type to get to it).  If type G inherits from F:

type G struct {
   F
}

then g.foo() has a depth of 2 – you there’s no foo() directly on G, but there is one on the anonymous field F.  Remember, g.foo() is really just an alias for g.F.foo() – count the points, and you have the depth.  And this is where the magic happens.  So consider:

type Dog struct {}
func (d Dog) bark() {}
func (d Dog) wag() {}

type Fox struct {}
func (f Fox) bark() {}
func (f Fox) burrow() {}

Now say you want (for some reason) to have a Gopher object that barks and wags like a Dog, but also burrows like a Fox?

type Gopher struct {
    Dog
    Fox
}

This is illegal: the rules of Go say you can’t have two functions at the same depth, and the bark()s here conflict. In this case, you have two depth-2 barks().  But here’s what you can do:

type deeperFox struct {
    Fox
}
type Gopher struct {
    Dog
    deeperFox
}

Now, the Fox functions are one deeper than the Dog functions; it’s now legal, the compiler is happy, and it is clear which bark() will be called.  If you really mean Fox.bark(), you can still get at it with g.deeperFox.bark().

When I first came across this in code, I couldn’t figure out why someone would do this.  The Go specification section on selectors wasn’t a whole lot of help in this case, at least for me.  I find this sort of thing a little bit hacky, but it works, it doesn’t require a lot of code, and it is straight-forward.  Much like the rest of Go.