40

I'm curious if this is possible in Go. I have a type with multiple methods. Is it possible to have a function which takes a method argument and then call it for the type?

Here is a small example of what I would want:

package main

import (
    "fmt"
)

type Foo int

func (f Foo) A() {
    fmt.Println("A")
}
func (f Foo) B() {
    fmt.Println("B")
}
func (f Foo) C() {
    fmt.Println("C")
}

func main() {
    var f Foo
    bar := func(foo func()) {
        f.foo()
    }
    bar(A)
    bar(B)
    bar(C)
}

Go thinks type Foo has a method called foo(), rather than replacing it with the passed in method name.

Dave C
  • 7,729
  • 4
  • 49
  • 65
dangeroushobo
  • 1,291
  • 2
  • 16
  • 28
  • I don't understand what your asking. Is this what you're trying to do? https://play.golang.org/p/VXuSOeGPd- – JimB Aug 11 '16 at 13:22

3 Answers3

61

Yes, it's possible. You have 2 (3) options:

Spec: Method expressions

The expression Foo.A yields a function equivalent to A but with an explicit receiver as its first argument; it has signature func(f Foo).

var foo Foo
bar := func(m func(f Foo)) {
    m(foo)
}
bar(Foo.A)
bar(Foo.B)
bar(Foo.C)

Here the method receiver is explicit. You only pass the method name (with the type it belongs to) to bar(), and when calling it, you have to pass the actual receiver: m(f).

Output as expected (try it on the Go Playground):

A
B
C

Spec: Method values

If f is a value of type Foo, the expression f.A yields a function value of type func() with implicit receiver value f.

var f Foo
bar := func(m func()) {
    m()
}
bar(f.A)
bar(f.B)
bar(f.C)

Note that here the method receiver is implicit, it is saved with the function value passed to bar(), and so it is called without explicitly specifying it: m().

Output is the same (try it on the Go Playground).

(For completeness: reflection)

Inferior to previous solutions (both in performance and in "safeness"), but you could pass the name of the method as a string value, and then use the reflect package to call the method by that name. It could look like this:

var f Foo
bar := func(name string) {
    reflect.ValueOf(f).MethodByName(name).Call(nil)
}
bar("A")
bar("B")
bar("C")

Try this on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • 2
    How would you do it for method with pointer receivers ? I tried like shown in this link https://play.golang.org/p/mdi8htBnZ9. But i cannot figure out. – Rajkamal Subramanian Nov 17 '17 at 00:16
  • As i could not edit previous comment, adding a new comment. I tried as given in spec and came up with https://play.golang.org/p/7P9mU0uhke. But i have one more question here. I was expecting the two addresses that are getting printed to be the same (address of Foo instance `f`) – Rajkamal Subramanian Nov 17 '17 at 00:31
  • 2
    @rajkamal Inside `foo.A()` you're not printing the `f` pointer but the address of the pointer. `f` is already a pointer, so `&f` will be of type `**foo`. If you change it to simply print the value of `f`, then you'll see the same pointer printed: [Go Playground](https://play.golang.org/p/kfrOQVC494) – icza Nov 17 '17 at 08:11
1

I played with this wanting a state machine type component that doesn't use a map[string]func() for containing state methods. My code allows a run method which is a simple for loop which breaks when pfunc == nil

type Foo struct {
    name string
    idx  int
}
type X func(*Foo) X

func (f *Foo) A() X {
    f.name += fmt.Sprintf(" A[%d]", f.idx)
    fmt.Println(f.name)
    if f.idx > 10 {
        fmt.Println("Foo is complete!")
        return nil
    } else {
        f.idx += 1
        return (*Foo).B
    }
}
func (f *Foo) B() X {
    f.name += fmt.Sprintf(" B[%d]", f.idx)
    fmt.Println(f.name)
    f.idx += 2
    return (*Foo).C
}
func (f *Foo) C() X {
    f.name += fmt.Sprintf(" C[%d]", f.idx)
    fmt.Println(f.name)
    f.idx += 3
    return (*Foo).A
}

func main() {
    bar := &Foo{"Ready!", 0}
    pfunc := (*Foo).A
    except := 1
    for pfunc != nil && except < 10 {
        pfunc = pfunc(bar)
        except += 1
    }
}    

Output:
Ready! A[0]
Ready! A[0] B[1]
Ready! A[0] B[1] C[3]
Ready! A[0] B[1] C[3] A[6]
Ready! A[0] B[1] C[3] A[6] B[7]
Ready! A[0] B[1] C[3] A[6] B[7] C[9]
Ready! A[0] B[1] C[3] A[6] B[7] C[9] A[12]
Foo is complete!

pmg7670
  • 101
  • 5
0

You could also use the “method values” option listed by @icza with different receivers.

package main

import "fmt"

type Foo int
type Goo int

func (f Foo) A() { fmt.Println("A") }
func (f Foo) B() { fmt.Println("B") }
func (g Goo) A() { fmt.Println("A") }

func main() {
    //Method values with receiver f
    var f Foo
    bar2 := func(m func()) { m() }
    bar2(f.A) //A
    bar2(f.B) //B
    //Method values with receivers f and g
    var g Goo
    bar2(f.A) //A
    bar2(g.A) //A
}