Adding Methods to Function Types in Go

May 5 2014

One of the intriguing features of Go is its ability to take a function type and attach methods to it. Yes, you read that right: in Go, functions can have methods. With this ability, you take a function and satisfy one or more interfaces by implementing methods on it.

I'd seen this little trick in the HTTP library, but never really taken the time to fully digest it until Gophercon, when Lann Martin, Sam Boyer, Chris and I were chatting about the highlights of the conference.

While it initially sounded like this feature was cool-but-not-useful, the conversation soon uncovered at least one interesting use case. Multiple interfaces can be satisfied with a function type (as opposed to, say, a struct). This differs a little bit from the net/http package, but I think it's an interesting case nonetheless.

Here's a sort of silly, but relatively straightforward example: Say we have a function type (Stringy) and an interface(StringPrinter), and we want the Stringy function type to not only do its thing (return a string), but also implement StringPrinter, whose signature is different than Stringy's.

Here's what the StringPrinter interface looks like:

package main

// StringPrinter is an interface that takes a string and (presumably) writes
// it to some destination.
type StringPrinter interface {
    PrintString(string)
}

We can imagine a case where a struct that wraps, say, a file satisfies the StringPrinter interface by supplying a method that prints to a file object attached to the struct. But rather than a struct, say we have a single function that does most of what we want.

Here's an example function type called Stringy:

// Stringy takes a string and returns a possibly different string.
type Stringy func(string) string

So we have a function type that... well... does stuff with strings. But say we'd like to make it do stuff with strings and print it to StdOut. We could do that in a way that satisfies StringPrinter!

// PrintString declares a method on the Stringy function.
// This way we can make our function match the StringPrinter
// interface.
func (s Stringy) PrintString(str string) {
    println(s(str))
}

Notice that the receiver on PrintString is a function of type Stringy. And the implementation of PrintString is pretty basic. But the cool thing here is that we satisfy StringPrinter() simply by running our String() function and passing the results to println().

At this point we have our Stringy function type, and we have our PrintString method, which implements StringPrinter. Now let's see how this works. We can create a Stringy function and call its PrintString() method:

func main() {

    var myStringy Stringy = func(str string) string {
        return "Got " + str
    }

    myStringy.PrintString("Test")
}

The above will print Got Test to StdOut.

To see why this is powerful, let's look at a similar snippet that makes things a little more obvious. This better demonstrates how a function can be converted into a Stringy, and then we can also assert that it's a StringPrinter:

// AnotherString is a function that matches the Stringy declaration.
func AnotherStringy(str string) string {
    return "Also got" + str
}

// Modified to show another way of doing this.
func main() {
    myStringy := Stringy(AnotherStringy)

    sp := StringPrinter(myStringy)

    sp.PrintString("Test 2")
}

In the example above, we worked more explicitly with the type system in order to illustrate how this new AnotherStringy function worked with both our Stringy and our StringPrinter. If we look back at the way HandlerFunc works in the net/http package in Go, we can see something happening there that is similar to the simple example above. (Though in that case, the function is not being used to satisfy another interface. It's just converted to a HandlerFunc)

True, this language feature may not be frequently required, but it is yet another way that we can interestingly and concisely solve problems without unnecessarily unwieldy code.