-2

How can I use variable substitution in go?

For example, I have the following code:

debug_level := "Info"
log.Info("Debug Test")

Can I use a variable debug_level for the log function instead of passing a direct argument? Something like:

debug_level := "Info"
log.${debug_level}("Debug Test")

Thank you.

Ilie Soltanici
  • 377
  • 2
  • 16
  • What you are trying to achieve is creating a function name dynamically by substituting the value or passing the value to golang function dynamically. Because first one is not possible though second can be. – Himanshu Aug 02 '18 at 10:27

2 Answers2

4

Go is a static language, not only all its variable, including functions, is statically typed, but also all functions are statically compiled and link. That said, it is not possible to calling a function dynamically according to its name without any preparation.

Methods of doing so including having a function viarable, as @icza provides, using a map that maps the name to the function, using reflect (limited to methods, at least for now), or using plugins and 3rd party loader to load pre-compiled Go packages.

From what I see from your use case, it is the simplest to use a map.

var logFn = map[string]func(...interface{}) {
    "Info": log.Info,
    "Debug": log.Debug,
}

By the way, it really does not make sense to write log according to using logging level.

Example: https://play.golang.org/p/drQzD1OH5ul

I cannot think of a single case that you cannot create such a map since all packages are statically linked in Go.

leaf bebop
  • 7,643
  • 2
  • 17
  • 27
  • It is different from what OP asked, If I take a view on the context. This is not substitution this is just assigning the values to map with functions as values and then calling the function using the keys of that map. It is not substitution which is I think not possible in Golang. Not for functions atleast, for strings it is though. – Himanshu Aug 02 '18 at 10:33
  • I think this can satisfy what OP's need. And if he really wants that kind of substitution, there is also reflect, provide it is a method of a type. – leaf bebop Aug 02 '18 at 10:36
  • Yeah it can do what OP wants only if it is what OP actually wants. Can you show us how can we achieve it using reflection, it will be helpful. – Himanshu Aug 02 '18 at 10:38
  • [`reflect.Value.MethodByName`](https://golang.org/pkg/reflect/#Value.MethodByName) – leaf bebop Aug 02 '18 at 10:42
  • That is used to return the value not to substitute the value. Reflection cannot modify the function name dynamically. Golang is static type language. What OP asked works with dynamic typed languages like PHP. What you have did is replaced the whole function using the map key not substituted the function inside package log. – Himanshu Aug 02 '18 at 10:44
  • I did not see a single word about changing function name. Why does OP or anyone else want to change a function's name? And from the use case `log.$(debug_level)`, is it implied that there are any function or function name changed with that syntax ? – leaf bebop Aug 02 '18 at 10:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/177273/discussion-between-himanshu-and-leaf-bebop). – Himanshu Aug 02 '18 at 10:52
  • It is not changing function name it is dynamically substituting function name. That's what OP asked. – Himanshu Aug 02 '18 at 10:52
  • @Himanshu DO NOT EDIT TO PROVIDE AN INFO THAT HAS NOTHING TO DO WITH THIS ANSWER. – leaf bebop Aug 02 '18 at 10:53
  • You are providing an answer that does not satisfy what OP asked. If it is not possible in Golang just mention it. You have provided a good turn around to achieve that but not the answer. – Himanshu Aug 02 '18 at 10:56
  • I failed to see the difference between `log.$(debug_level)` and `logFn[debug_level]`. Provide an example that what OP asked cannot be acheived by my answer or further comments will be ignored. – leaf bebop Aug 02 '18 at 10:59
  • Your way is not wrong it is correct. But It is not substitution, it is calling the function using map keys. First one is substituting the value and other one is using the value at that defined key. So there is a big difference syntactically. – Himanshu Aug 02 '18 at 11:03
  • Ok I get what you mean. You think I should clearify on the fact that Go is a static language without macros or parsing. Right. – leaf bebop Aug 02 '18 at 11:06
4

Go is a statically typed language, if you would call a function or method by name, the compiler could not check if the parameters you provide match the signature of the function.

Instead use function variables: your variable that currently holds the method or function name can be be a variable of function type, holding a function or method value.

Let's assume we have the following logging functions:

func Info(args ...interface{}) {
    fmt.Print("[Info] ")
    fmt.Println(args...)
}

func Error(args ...interface{}) {
    fmt.Print("[Error] ")
    fmt.Println(args...)
}

You may use it like this:

var logger func(...interface{}) = Info

func main() {
    logger("something")
    logger = Error
    logger("Some other thing")
}

Output will be (try it on the Go Playground):

[Info] something
[Error] Some other thing

Also note that this works with methods too, not just with functions:

type Logger struct{}

func (l Logger) Info(args ...interface{}) {
    fmt.Print("[Info] ")
    fmt.Println(args...)
}

func (l Logger) Error(args ...interface{}) {
    fmt.Print("[Error] ")
    fmt.Println(args...)
}

Using it:

var mainLogger = Logger{}

var logger func(...interface{}) = mainLogger.Info

func main() {
    logger("something")
    logger = mainLogger.Error
    logger("Some other thing")
}

Output (try it on the Go Playground):

[Info] something
[Error] Some other thing

See related question: golang function alias on method receiver

icza
  • 389,944
  • 63
  • 907
  • 827
  • These can be done with user defined functions If I am not wrong and then calling the predefined functions like log.Fatal inside them. We cannot do that with predefined functions or Do we ? – Himanshu Aug 02 '18 at 11:07
  • I am asking that `log.${debug_level}` is what OP has asked for substitution to call golang package `log.Fatal` or `log.Println`. Can we do that also. – Himanshu Aug 02 '18 at 11:10
  • This `func (l Logger) Error(args ...interface{}) { fmt.Print("[Error] ") fmt.Println(args...) }` is user defined function right in which you can call any log functions – Himanshu Aug 02 '18 at 11:13
  • @Himanshu With reflection, you can call methods by name. Functions, you can't. [Details.](https://stackoverflow.com/questions/37384473/call-functions-with-special-prefix-suffix/37384665#37384665) – icza Aug 02 '18 at 11:15