Isarcus
Isarcus

Reputation: 33

Passing a struct method as an argument

I am not sure if I'm trying to exceed the limits of what Go will allow, but here is the context: I have type called Map with a hefty number of helpful member functions, such as Interpolate, Add, and Clear. I also have a struct type, Layered, that contains several Maps.

type Map [][]float64

func (m Map) Interpolate(min, max float64) { ... }
func (m Map) Add(val float64) { ... }
func (m Map) Clear() { ... }

type Layered struct {
    data []Map
}

I would like to add a member function to Layered that accepts any member function of Map as an argument, and then calls it on all of its contained Maps. Something along the lines of:

func (l Layered) MapCall(callMe func(Map)) {
    for _, m := range l.data {
        callMe(m)
    }
}

This partially works, but only for member functions of Map that take no additional arguments, such as Clear. Is it at all possible to accept any member function of Map as an argument (along with its potential parameters in the form of an ...interface{}), and then call the function, with its correct parameters, on all of the member Maps in a Layered? Something like this, for example:

func (l Layered) MapCall(callMe func(Map, ...interface{}), params ...interface{}) {
    for _, m := range l.data {
        callMe(m, params...)
    }
}

Upvotes: 0

Views: 1208

Answers (1)

Tyler Kropp
Tyler Kropp

Reputation: 573

You may want to be more functional than try to use interfaces.

I will provide an example using your Interpolate method.

You can start by defining a helper function for generating the operation:

func InterpolateOp(min, max float64) func(Map) {
    return func(m Map) {
        m.Interpolate(min, max)
    }
}

Another name that would match your MapCall function better is MapInterpolate.

Then, when using MapCall or creating lists of operations, you can use the same type you were using previously, namely func(Map):

var l Layered
...
l.MapCall(InterpolateOp(2, 4))

The key to this solution is that the data is bound to the closure that makes up the function, which solves the original problem: not enough data to call the appropriate functions.

In your case, if there are so many methods that this is not worth doing, then you may look into code generation, since it is repetitive and easy. That is outside the scope of this question.

See also: https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions

Upvotes: 1

Related Questions