1

I am a Go beginner, coming from Ruby land.

In Ruby, you could do something like this.

Time.send("now") is equivalent to Time.now, as you are sending the message now to the object Time

Is there something similar in golang?

samol
  • 18,950
  • 32
  • 88
  • 127
  • I'm afraid Go is neither a dynamic nor a classically object oriented language. A lot of things are different here. – fuz Sep 26 '13 at 09:05
  • I don't think you've accurately described `send`. It looks like the first argument of `send` needs to be a symbol not a string. – deft_code Sep 27 '13 at 06:33
  • @deft_code It works with both `Time.send("now")`, `Time.send(:now)` -- also ruby isn't picky about these kinds of things – Yo Ludke Jul 31 '19 at 06:34

5 Answers5

9

There is no built in way of calling an arbitrary function from a string in Go.

You can create something similar by registering functions to a map[string]. A working example:

package main

import "fmt"

var m = map[string]func(){
    "now":  func() { fmt.Println("The time is now") },
    "then": func() { fmt.Println("Once upon a time") },
}

func main() {
    cmd := "then"
    m[cmd]()
}

play.golang.org

There is also the possibility of using reflection in order to call a method by name. You can look at the reflect package for MethodByName and Call. You can also check this Stackoverflow question.

Community
  • 1
  • 1
ANisus
  • 74,460
  • 29
  • 162
  • 158
6

As other suggested, you can do it yourself by mapping strings to functions, but the strong-typing nature of Go makes it difficult to translate .send directly into Go.

You can still use reflection if you really need to access a field or method by name:

import "reflect"
import "fmt"

type A struct {
    Number int
}

func (a *A) Method(i int) int {
    return a.Number + i;
}

func main() {
    a := &A{Number: 1}
    // Direct access
    fmt.Printf("Direct -> Nb: %d, Nb + 2: %d\n", a.Number, a.Method(2));

    v := reflect.ValueOf(*a)
    vp := reflect.ValueOf(a)
    field := v.FieldByName("Number")
    meth := vp.MethodByName("Method")
    args := []reflect.Value{reflect.ValueOf(2)}
    // Reflection access
    fmt.Printf("Reflect -> Nb: %d, Nb + 2: %d\n", 
               field.Interface().(int), 
               meth.Call(args)[0].Interface().(int))
}

Outputs:

Direct -> Nb: 1, Nb + 2: 3
Reflect -> Nb: 1, Nb + 2: 3

play.golang.org

Note however:

  1. How cumbersome that is. Usually, performing a map as suggested by @ANisus is a more idiomatic way of doing
  2. You still have to perform your conversions in the end.

Using the reflect packages changes your typed variable into more flexible Value objects, but these are very cumbersome to use in practice. It is usually better if you can find a way to express your intent without relying on reflection.

Also note that here, we had to use two Values, one for a (a pointer to A) for the method, and one for *a (a A structure) for the field. Trying to get a method defined with a pointer receiver with a non-pointer Value (or conversely, trying to obtain a field via a pointer Value) will result in a panic. More generally, due to the dynamic nature of reflected Values and its difference with the usual typed Go, expect a lot of convenience features (such as automatic referencing/dereferencing) to be absent on Values.

Also, expect quite a bit of runtime panics while debugging, as it is the only way for dynamic Value calls to fail !

Reference: the reflect package

val
  • 8,459
  • 30
  • 34
1

No. Work your way through http://tour.golang.org/ and http://golang.org/doc/effective_go.html and you will have a proper understanding of how method invocation works.

Volker
  • 40,468
  • 7
  • 81
  • 87
1

Here is a working example using reflect

package main

import (
    "fmt"
    "os"
    "reflect"
)

// Send sends a message to(calls a method of) obj, with args.
// The return value of the method call is set to ret and any error to err.
func Send(obj interface{}, method string, args ...interface{}) (ret []reflect.Value, err error) {
    defer func() {
        if e := recover(); e != nil {
            err = fmt.Errorf("%v", e)
        }
    }()
    objValue := reflect.ValueOf(obj)
    argsValue := make([]reflect.Value, 0, len(args))
    for _, arg := range args {
        argsValue = append(argsValue, reflect.ValueOf(arg))
    }
    mtd := objValue.MethodByName(method)
    if !mtd.IsValid() {
        return nil, fmt.Errorf("%v does not have a method %v", reflect.TypeOf(obj), method)
    }
    ret = mtd.Call(argsValue)
    return
}

// Then do some tests.

type A struct {
    value int
}

func (a A) Value() int {
    return a.value
}

func (a *A) SetValue(v int) {
    a.value = v
}

func main() {
    var (
        ret []reflect.Value
        err error
    )
    // StdOut.WriteString("Hello, World!\n")
    _, err = Send(os.Stdout, "WriteString", "Hello, World!\n")
    handleError(err)

    var a = &A{100}

    // ret = a.Value()
    ret, err = Send(a, "Value")
    handleError(err)
    fmt.Printf("Return value is: %v\n", ret[0].Int())

    // a.SetValue(200)
    _, err = Send(a, "SetValue", 200)
    handleError(err)

    // ret = a.Value()
    ret, err = Send(a, "Value")
    handleError(err)
    fmt.Printf("Return value is: %v", ret[0].Int())
}

func handleError(err error) {
    if err != nil {
        panic(err)
    }
}
Kevin Yuan
  • 1,008
  • 8
  • 20
0

I based my code on this description of send.

class Klass
  def hello(*args)
    "Hello " + args.join(' ')
  end
end
k = Klass.new
k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

http://play.golang.org/p/lXlzBf_fGZ

package main

import "strings"

type Klass struct{}

func (k Klass) Hello(args ...string) string {
    return "Hello " + strings.Join(args, " ")
}

func (k Klass) Send(symbol func(Klass, ...string) string, args ...string) string {
    return symbol(k, args...)
}

func main() {
    k := new(Klass)
    k.Send(Klass.Hello, "gentle", "readers") //=> "Hello gentle readers"
}

The big difference between the two is that Go's Send function is only implemented for Klass and only works on methods that take a variable number of strings as parameters and return a single string. This is because Go is a statically typed language where Ruby is dynamically typed. Go does support dynamic typing via the reflect library, but it is an unpleasant experience and not the way general Go code is meant to be written.

deft_code
  • 57,255
  • 29
  • 141
  • 224