a73x

high effort, low reward

← Posts

Simplifying Interfaces with Function Types

Table of Contents

In Go, you can define methods on type aliases, which means that we can define a type alias of a function, and then define methods on that function.

Example

Given the following interface

type DB interface {
	Get(string) (string, error)
}

You can fulfill it using a function type like this:

type GetFn func(string) (string, error)

func (f GetFn) Get(a string) (string, error) {
	return f(a)
}

Now you can use GetFn whenever a DB is required:

func main() {
	var storeFn DB = GetFn(func(s string) (string, error) {
		return "bar", nil
	})
	fmt.Println(storeFn.Get("Foo")) // Outputs: bar
}

You can try this example in this [Go Playground](https://go.dev/play/p/hyBNIMblafs

How it works

In Go, interfaces are implicitly through method sets, which means any type (including a function type) that defines the required methods satisfies the interface. By defining the Get method on the GetFn type, the compiler treats GetFn as a valid implementation of the DB interface.

This flexibility allows you to use function types as lightweight, dynamic implementations of interfaces, without the need for full struct definitions.

Application

One common use case for this pattern is testing. Instead of implementing a full mock, you can use an inline function to provide test specific behavior.

func TestHandler() {
    mockDB := GetFn(func(key string) (string, error) {
        if key == "foo" {
            return "bar", nil
        }
        return "", fmt.Errorf("not found")
    })

    result, err := mockDB.Get("foo")
    fmt.Println(result, err) // Outputs: bar, <nil>
}

This approach is not limited to testing. It’s also useful for dependency injection, where you can pass in lightweight or context specific implementations of an interface.

This pattern is similar to how http.HandleFunc works. In the HTTP package, http.HandlerFunc is a function type that fulfills the http.Handler interface by implementing its ServeHTTP method. This allows functions to act as handlers, providing great flexibility in designing web servers.