💾 Archived View for ser1.net › post › go-revelations.gmi captured on 2024-06-16 at 12:42:02. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-03-21)

-=-=-=-=-=-=-

--------------------------------------------------------------------------------

title: "Go revelations" date: 2012-03-02T21:46:00Z tags: ["golang", "programming", "software"]

--------------------------------------------------------------------------------

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[1] 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.

1: http://golang.org/doc/go_spec.html#Selectors