Passing an arbitrary function as a parameter in Go - go

I'm trying to expand my knowledge of Go's function pointers, and I have a question about what is and is not possible with passing functions as parameters in Go.
Let's say that I want to write a decorator() function that can wrap any existing function. For simplicity, let's limit this to functions that accept exactly one parameter and return exactly one value.
If I write a decorator that accepts func(interface{}) interface{} as it's argument, it will implicitly work as long as that function I pass in also accepts/returns an interface{} type (see funcA).
My question is--is there a way to convert an existing function of type func(string) string to a type of func(interface{}) interface{} so that it can also be passed into a decorator function without just wrapping it in a new anonymous function (see funcB)?
package main
import (
"fmt"
)
func decorate(inner func(interface{}) interface{}, args interface{}) interface {} {
fmt.Println("Before inner")
result := inner(args)
fmt.Println("After inner")
return result
}
func funcA(arg interface{}) interface{} {
fmt.Print("Inside A, with arg: ")
fmt.Println(arg)
return "This is A's return value"
}
func funcB(arg string) string {
fmt.Print("Inside B, with arg: ")
fmt.Println(arg)
return "This is B's return value"
}
func main() {
// This one works. Output is:
//
// Before inner
// Inside A, with arg: (This is A's argument)
// After inner
// This is A's return value
//
fmt.Println(decorate(funcA, "(This is A's argument)"))
// This doesn't work. But can it?
//fmt.Println(decorate(funcB, "(This is B's argument)"))
}

This is not possible. One reason for that is the mechanics of passing parameters differ from function to function, and using an interface{} arg does not mean "accept anything". For example, a function taking a struct as an arg will receive each member of that struct, but a function taking an interface{} containing that struct will receive two words, one containing the type of the struct, and the other containing a pointer to it.
So, without using generics, the only way to implement this is by using an adapter function.

Use the reflect package to handle functions with arbitrary argument and result types.
func decorate(inner interface{}, args interface{}) interface{} {
fmt.Println("Before inner")
result := reflect.ValueOf(inner).Call([]reflect.Value{reflect.ValueOf(args)})
fmt.Println("After inner")
return result[0].Interface()
}
Run the code on the playground.
Like the decorate function in the question, the function in this answer assumes one argument and one result. The function must be modified to handle other function types.
The OP should consider the tradeoffs between the anonymous wrapper function proposed in the question and the use of the reflect package here. Calling the function through the reflect API is slower than calling the function through the anonymous wrapper. There's also a loss of type safety with the reflect API. The anonymous wrapper function adds verbosity.

For the record, with Go 1.18 and the introduction of generics, the decorator function becomes almost trivial.
You may declare a type constraint as such:
type UnaryFunc[T any] interface {
func(T) T
}
The constraint itself is parametrized with T to allow for unary functions that take and return arbitrary types.
In the decorate function you then instantiate the constraint with a type parameter. The signature becomes:
decorate[T any, F UnaryFunc[T]](inner F, arg T) T
Thanks to type inference, you can just pass concrete arguments to the function, and both T and F will be unambiguous.
Example alternatives without a named constraint:
// accept and return T
decorate[T any](inner func(T) T, arg T) T
// only return T
decorate[T any](inner func() T) T
// return T and error
decorate[T any](inner func(T) (T, error), arg T) (T, error)
// N-ary function
decorate[T, U any](inner func(T, U) (T, error), argt T, argu U) (T, error)
The obvious limitation is that the interface constraint UnaryFunc specifies only functions that take and return exactly one arg of type T. You can't do otherwise, because the type set of an interface constraint may include types which support the same operations — and calling with one arg is not compatible with calling with N args.
The full program:
package main
import (
"fmt"
)
type UnaryFunc[T any] interface {
func(T) T
}
func decorate[T any, F UnaryFunc[T]](inner F, arg T) T {
fmt.Println("before inner")
result := inner(arg)
fmt.Println("after inner")
return result
}
func funcA(arg int) int {
fmt.Println("inside A with:", arg)
return arg
}
func funcB(arg string) string {
fmt.Println("inside B with:", arg)
return arg
}
func main() {
// this works
decorate(funcA, 200)
// this also works
decorate(funcB, "Func B")
}
Playground: https://go.dev/play/p/3q01NiiWsve

is there a way to convert an existing function of type func(string) string to a type of func(interface{}) interface{} so that it can also be passed into a decorator function without just wrapping it in a new anonymous function (see funcB)?
No. It's that simple: No.

Related

Assign "func returning pointer to struct" to var of type "func returning pointer to interface" [duplicate]

This question already has an answer here:
cannot use function (type func()) as type in argument
(1 answer)
Closed 2 years ago.
I have two functions which return pointers to two separate structs conforming to the same interface. How can I put the functions in the same map? I came up with creating wrapper functions (getFooer in the example) to make the types check. Is there a better way? What are the rules that make types check for the type conversion in the getFooer function, but not for the type conversion in the commented out line in main?
package main
import (
"fmt"
)
type Fooer interface {
Foo()
}
type A struct {
}
func (a *A) Foo() {
}
var a A = A{}
func getA() (*A) {
return &a
}
func getFooer() (Fooer) {
return getA()
}
func main() {
var f func() (Fooer)
// f = getA // /tmp/foo.go:29:7: cannot use getA (type func() *A) as type func() Fooer in assignment
f = getFooer
fmt.Println(f)
}
How can I put the functions in the same map?
You cannot, they have different types.
I came up with creating wrapper functions (getFooer in the example) to make the types check. Is there a better way?
Basically: No. You could modify the signature of e.g. getA to getA() Fooer but that would require type assertions back to *A if you need an A.
What are the rules that make types check for the type conversion in the getFooer function, but not for the type conversion in the commented out line in main?
Dead simple: A function getA() *A hase type func() *A and this type is different from func() Fooer (no covariance). Functin types must match literally. You can return a *A as a Fooer because you can assign a *A to a variable of type Fooer. The getA yields a *A and this *A is assigned to the return value of getFooer which is a Fooer.

How HandlerFunc(f) convert a function to an interface type?

When check following code, got a doubt with type convert from function to interface.
Code
http_hello.go:
package main
import (
"fmt"
"log"
"net/http"
)
// hello http,
func helloHttp() {
// register handler,
http.Handle("/", http.HandlerFunc(helloHandler))
// start server,
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
// handler function - hello,
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
}
func main() {
helloHttp()
}
The above code works.
(Then I tried to write a small program to check is this a general feature, but it won't work, check following code)
func_to_intf.go:
package main
import (
"fmt"
)
// an interface,
type Adder interface {
add(a, b int) int
}
// alias of a function signature,
type AdderFunc func(int, int) int
// a simple add function,
func simpleAdd(a, b int) int {
return a + b
}
// call Adder interface to perform add,
func doAdd(a, b int, f Adder) int {
return f.add(a, b)
}
func funcToIntf() {
fa := AdderFunc(simpleAdd)
fmt.Printf("%#v, type: %T\n", fa, fa)
a, b := 1, 2
sum := doAdd(a, b, fa)
fmt.Printf("%d + %d = %d\n", a, b, sum)
}
func main() {
funcToIntf()
}
Output:
./func_to_intf.go:30:14: cannot use fa (type AdderFunc) as type Adder
in argument to doAdd: AdderFunc does not implement Adder (missing add
method)
Questions
http.HandlerFunc(helloHandler) get a value of type http.Handler, since that's what http.Handle() expect, is that correct?
If yes, then means it convert a function into a value of an interface type, how did that happen?
Is this a built-in feature of go?
I did a test (as in func_to_intf.go above), and seems not.
Or, is http.HandlerFunc's special implementation achieve that?
#Update - Summary
(Though the answer(s) addressed the questions pretty well, but after reviewing & more testing, there are several other go features required to totally erase the original doubt, as following.)
Function type.
Function is value, and it has type.
Function type could be defined via type keyword on a function signature.
e.g type AdderFunc func(int, int) int
Type convertor T(v) on function.
Any function could be converted to a function type with the same signature, just via T(v), use function type name as T, and actual function as v.
Then when the new value is called, the actual function v is called.
e.g fa := AdderFunc(simpleAdd)
(this is blur to me before asking the question, and that's one of the main reason I was confused).
It is a simple type-conversion.
In Go you can define custom type besides structs. In this case, http.HandlerFunc is a function type, func(http.ResponseWriter,*http.Request). Since your function is of the same underlying type (signature) as the custom type, it can be converted to it.
Furthermore, code can define methods on custom type, no matter what underlying type it is, or whether it is a struct or not. In this case, http package defines ServeHTTP method on it, and of course, it just calls the function itself.
You can read the source code here: https://golang.org/src/net/http/server.go?s=58384:58444#L1936
As for the adder in your sample code, you can do the same: Define a method on AdderFunc.
func (a AdderFunc) add(x, y int) int {
return a(x, y)
}
Playground: https://play.golang.org/p/5mf_afHLQA2
http.HandlerFunc is a type that satisfy interface http.Handler by providing method http.ServeHTTP(ResponseWriter, *Request).
http.HandlerFunc(helloHandler) is a type conversion which convert types with same underlying base type but different method set.
You example working
Not special or built-in feature. Instead http.HandlerFunc is a type that implements the http.Handler interface. Have a look at its implementation which is quite nifty https://github.com/golang/go/blob/d3c3aaa61f7598f275f30fabd3749379fe0f2720/src/net/http/server.go#L1956
As the docs say:
The HandlerFunc type is an adapter to allow the use of ordinary functions as HTTP handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f.
So, it's not a function, but a wrapper type, declared as:
type HandlerFunc func(ResponseWriter, *Request)
But Go allows (and that's one of its greatest features, in my opinion) to make any newly declared type implement any possible interface just by defining the require method. So the type HandlerFunc implements the interface Handler by defining the method ServeHttp. The implementation just calls the wrapped function.

Function types in Go - particular type casting to more general type

What cast / assertion need I do in Go in order to pass to a function expecting a generic function like func(interface{}) interface{}, a more specific function like func(int) int instead?
For example, in code like this, fooA can be passed to MakeExclamer, but not fooB:
func MakeExclamer(foo func (interface{}) interface{}, n int) func () {
return func() {
fmt.Printf("%v!!!", foo(n))
}
}
func fooA(x interface{}) interface{} {
return x.(int)*2
}
func fooB(x int) int {
return x * 10
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
// >> cannot use fooB (type func(int) int) as type func(interface {}) interface {} in argument to MakeExclamer
exclamerB()
}
(Go Playground link: https://play.golang.org/p/xGzfco0IAG)
I'm not interested much in alternative code structure patterns, since this is how I want it to work: a specific function should be passed to a general function transformer (accepting function of type Any -> Any) that will return another general function (Any -> Any). This may not be idiomatic in Go, but it is the pattern that I want my code to follow.
To use type assertions, every possible type must be enumerated in MakeExclamer:
func MakeExclamer(fn interface{}, arg interface{}) func() {
switch fn := fn.(type) {
case func(int) int:
return func() {
fmt.Printf("%v!!!\n", fn(arg.(int)))
}
case func(interface{}) interface{}:
return func() {
fmt.Printf("%v!!!\n", fn(arg))
}
default:
panic("not supported")
}
}
To accept a function of any type, the fn argument is declared as type interface{}. The code uses a type switch to handle the different function types.
playground example
Reflection can be used to write a more general function.
func MakeExclamer(fn interface{}, arg interface{}) func() {
fnr := reflect.ValueOf(fn)
argr := reflect.ValueOf(arg)
return func() {
resultr := fnr.Call([]reflect.Value{argr})
fmt.Printf("%v!!!\n", resultr[0].Interface())
}
}
playground example
First things first : When it comes to typing in Go, everything is theoretically possible. That's because even though the compiler does a lot of checks at compile-time, it is possible to change the runtime... at runtime. So-called runtime hacks, where you dynamically manipulate runtime structs that you're NOT supposed to handle.
Now, you have an interesting question, whose answer doesn't include the need to use the 'unsafe' package. However, the way I found of generalizing a function involves heavy reflection.
How to call a function (via reflection) ?
The documentation for the reflect package can be found here.
So, like all elements in Golang, functions have a Type. Without going through all fields, functions do take an array of arguments and produce an array of results. It is possible to investigate the Type of arguments and results through the In(int) and Out(int) method.
func investigate(fn interface{}) {
fnType := reflect.TypeOf(fn)
for idx := 0; idx < fnType.NumIn(); idx ++ {
fmt.Printf("Input arg %d has type %v\n", idx, fnType.In(idx))
}
for idx := 0; idx < fnType.NumOut(); idx ++ {
fmt.Printf("Output arg %d has type %v\n", idx, fnType.Out(idx))
}
}
We won't use this code. However, two important things are to be noted at this point :
The generic type under which a function can be passed around without caring about its type is interface{}. Something like "func(interface{}) interface{}" is not a generalization of a function, it is already a concrete type. Hence, "func(interface{}) interface{}" is not a generalization of "func(int) int", those are two different function types entirely. This is why you can't use type assertions/cast to convert from one function type to another.
A function can be represented as something that takes an input array and produces and output array.
Now, in order to call a function, you have to get not its Type, but its Value. Once you get its value, you can call it using an array of arguments, which must all be Values.
The prototype is:
func (v Value) Call(in []Value) []Value
Using this method, it is possible to call any function.
The code
So, the only thing you need is to convert whichever arguments array you have to an array of Values, then you will be able to call your function.
Here is your code:
package main
import (
"fmt"
"reflect"
)
func MakeExclamer(foo interface{}, n int) func() {
exclamer := generalize(foo, n)
return func() {
fmt.Printf("%v!!!\n", exclamer())
}
}
func fooA(x interface{}) interface{} {
return x.(int) * 2
}
func fooB(x int) int {
return x * 10
}
func generalize(implem interface{}, args ...interface{}) func() interface{} {
valIn := make([]reflect.Value, len(args), len(args))
fnVal := reflect.ValueOf(implem)
for idx, elt := range args {
valIn[idx] = reflect.ValueOf(elt)
}
ret := func() interface{} {
res := fnVal.Call(valIn)
// We assume the function produces exactly one result
return res[0].Interface()
}
return ret
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
exclamerB()
}
Playground
The important bit is the generalize function which makes the translation between your arguments and an array of Values, then returns a new function whith all parameters already filled.
Do not hesitate if you need any precision !

Golang methods with same name and arity, but different type

The following code works fine. Two methods operating on two different structs and printing a field of the struct:
type A struct {
Name string
}
type B struct {
Name string
}
func (a *A) Print() {
fmt.Println(a.Name)
}
func (b *B) Print() {
fmt.Println(b.Name)
}
func main() {
a := &A{"A"}
b := &B{"B"}
a.Print()
b.Print()
}
Shows the desired output in the console:
A
B
Now, if I change the method signature in the following way I get an compile error. I just move the receiver of the method to the arguments of the method:
func Print(a *A) {
fmt.Println(a.Name)
}
func Print(b *B) {
fmt.Println(b.Name)
}
func main() {
a := &A{"A"}
b := &B{"B"}
Print(a)
Print(b)
}
I can't even compile the program:
./test.go:22: Print redeclared in this block
previous declaration at ./test.go:18
./test.go:40: cannot use a (type *A) as type *B in function argument
Why is it that I can interchange struct types in the receiver, but not in the
arguments, when the methods have the same name and arity?
Because Go does not support overloading of user-defined functions on their argument types.
You can make functions with different names instead, or use methods if you want to "overload" on only one parameter (the receiver).
You can use type introspection. As a general rule, though, any use of the generic interface{} type should be avoided, unless you are writing a large generic framework.
That said, a couple of ways to skin the proverbial cat:
Both methods assume a Print() method is defined for both types (*A and *B)
Method 1:
func Print(any interface{}) {
switch v := any.(type) {
case *A:
v.Print()
case *B:
v.Print()
default:
fmt.Printf("Print() invoked with unsupported type: '%T' (expected *A or *B)\n", any)
return
}
}
Method 2:
type Printer interface {
Print()
}
func Print(any interface{}) {
// does the passed value honor the 'Printer' interface
if v, ok := any.(Printer); ok {
// yes - so Print()!
v.Print()
} else {
fmt.Printf("value of type %T passed has no Print() method.\n", any)
return
}
}
If it's undesirable to have a Print() method for each type, define targeted PrintA(*A) and PrintB(*B) functions and alter Method 1 like so:
case *A:
PrintA(v)
case *B:
PrintB(v)
Working playground example here.
You can not do function or method overloading in Go. You can have two methods with the same names in Go but the receiver of these methods must be of different types.
you can see more in this link .

Using function names as parameters

In Go, you can pass functions as parameters like callFunction(fn func). For example:
package main
import "fmt"
func example() {
fmt.Println("hello from example")
}
func callFunction(fn func) {
fn()
}
func main() {
callFunction(example)
}
But is it possible to call a function when it's a member of a struct? The following code would fail, but gives you an example of what I'm talking about:
package main
import "fmt"
type Example struct {
x int
y int
}
var example Example
func (e Example) StructFunction() {
fmt.Println("hello from example")
}
func callFunction(fn func) {
fn()
}
func main() {
callFunction(example.StructFunction)
}
(I know what I'm trying to do in that example is a little odd. The exact problem I have doesn't scale down to a simple example very well, but that's the essence of my problem. However I'm also intrigued about this from an academic perspective)
Methods (which are not "members of a struct" but methods of any named type, not only structs) are first class values. Go 1.0.3 didn't yet implemented method values but the tip version (as in the comming Go 1.1) has support method values. Quoting the full section here:
Method values
If the expression x has static type T and M is in the method set of type T, x.M is called a method value. The method value x.M is a function value that is callable with the same arguments as a method call of x.M. The expression x is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later.
The type T may be an interface or non-interface type.
As in the discussion of method expressions above, consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t T
var pt *T
func makeT() T
The expression
t.Mv
yields a function value of type
func(int) int
These two invocations are equivalent:
t.Mv(7)
f := t.Mv; f(7)
Similarly, the expression
pt.Mp
yields a function value of type
func(float32) float32
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mv is equivalent to (&t).Mv.
f := t.Mv; f(7) // like t.Mv(7)
f := pt.Mp; f(7) // like pt.Mp(7)
f := pt.Mv; f(7) // like (*pt).Mv(7)
f := t.Mp; f(7) // like (&t).Mp(7)
f := makeT().Mp // invalid: result of makeT() is not addressable
Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.
var i interface { M(int) } = myVal
f := i.M; f(7) // like i.M(7)
Go 1.0 does not support the use of bound methods as function values. It will be supported in Go 1.1, but until then you can get similar behaviour through a closure. For example:
func main() {
callFunction(func() { example.StructFunction() })
}
It isn't quite as convenient, since you end up duplicating the function prototype but should do the trick.
I fixed your compile errors.
package main
import "fmt"
type Example struct {
x, y float64
}
var example Example
func (e Example) StructFunction() {
fmt.Println("hello from example")
}
func callFunction(fn func()) {
fn()
}
func main() {
callFunction(example.StructFunction)
}
Output:
hello from example
To add to #zzzz great answer (and the one given at https://golang.org/ref/spec#Method_values) here is an example that creates a method value from a value of an interface type.
package main
import "fmt"
type T struct{}
func (T) M(i int) { fmt.Println(i) }
func main() {
myVal := T{}
var i interface{ M(int) } = myVal
f := i.M
f(7) // like i.M(7)
}

Resources