Writing a generic binder in Go - go

Consider the following function -- a binder of the second argument.
func bindSecond(value interface{}, f func(a interface{}, b interface{})) func (c interface{}) {
return func(arg interface{}) {
f(arg, value)
}
}
Consider a function
func f(x int, y int) {}
When trying to use it as a parameter in binder, go compiler says:
cannot use f (type func(int, int)) as type func(interface {}, interface {}) in argument to bindSecond.
Could you please suggest me the proper way of implementing binders in go (golang)?

Replace interface{} with int and it will work.
Or, more to the point, try to find a solution to your original problem using the features Go provides, instead of trying to force a solution from a different paradigm.

I see what you're trying to do, some templates, right? But this is not going to work this way. Your f func(a interface{}, b interface{}) argument is not an interface, so it must be of the matching type. You could achieve what you want by passing f interface{} and then manipulate it with reflect package.
Some naive example:
package main
import (
"fmt"
"reflect"
)
func bindSecond(value interface{}, f interface{}) func(c interface{}) {
return func(arg interface{}) {
reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(arg), reflect.ValueOf(value)})
}
}
func main() {
sum := func(x int, y int) { fmt.Println(x + y) }
inc := bindSecond(1, sum)
inc(10)
}
// Prints 11
Doable, but not very pretty. And panics in runtime if anything unexpected happens, for example f is not callable and doesn't take exactly two arguments. Or you pass a wrong type to inc. Complier won't help you anymore.

Related

Could adding variadic parameters to a function break existing code?

Is adding a variadic parameter to an existing Go function a breaking change?
For example:
// Old function
func Foo(a int)
// Updated to:
func Foo(a int, params ...string)
Callers of the API can omit the new parameter, so I would think the API is backwards-compatible.
Can anyone provide an example where a user of the old API could not use the new API without changing their code?
I. Changing functions
Calling them will continue to work without modification, but since the function signatures do not match, that may easily break some code.
For example (try it on the Go Playground):
func Foo(a int) {}
func Foo2(a int, params ...string) {}
func main() {
var f func(int)
f = Foo
f = Foo2 // Compile-time error!
_ = f
}
The line f = Foo2 produces a compile-time error:
cannot use Foo2 (type func(int, ...string)) as type func(int) in assignment
So this is a backward incompatible change, don't do it.
The above example gave a compile-time error, which is the lucky / better case, but there may also be code that would only fail at runtime (non-deterministic if / when that happens), like in this example:
func Foo(a int) {}
func Foo2(a int, params ...string) {}
func main() {
process(Foo)
process(Foo2) // This will panic at runtime (type assertion will not hold)!
}
func process(f interface{}) {
f.(func(int))(1)
}
Calling process(foo) succeeds, calling process(foo2) will panic at runtime. Try it on the Go Playground.
II. Changing methods
Your question was directed at functions, but the same "problem" exists with methods too (when used as method expressions or method values, for example see golang - pass method to function).
Additionally, this may break implicit interface implementations (it may make types not implement interfaces), like in this example (try it on the Go Playground):
type Fooer interface {
Foo(int)
}
type fooImpl int
func (fooImpl) Foo(a int) {}
type fooImpl2 int
func (fooImpl2) Foo(a int, params ...string) {}
func main() {
var f Fooer
f = fooImpl(0)
f = fooImpl2(0) // Compile time error!
_ = f
}
Because signatures don't match, fooImpl2 does not implement Fooer, even though fooImpl does:
cannot use fooImpl2(0) (type fooImpl2) as type Fooer in assignment:
fooImpl2 does not implement Fooer (wrong type for Foo method)
have Foo(int, ...string)
want Foo(int)

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 !

What's the point in wrapping a function in a function type?

I am new to golang and I've seen occasionally some code that wraps a function inside a function type. In http package we have this as well:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
I'm curious to know the reason behind. If we want to have a type that exposes a method why don't we create a struct type and add the method to it?
Two main reasons:
Convenience when receiving functions as arguments:
type LongFuncSig func(a int, b *int, c string, d *SomeWeirdThing, f map[string]*Foo, g ...interface{}) (*Result, error)
func DoSomething(fn LongFuncSig) { ... }
func DoSomethingElse(fn LongFuncSig) { ... }
func DoYetAnotherThing(fn LongFuncSig) { ... }
Is much more readable, and less error-prone than:
func DoSomething(fn func(a int, b *int, c string, d *SomeWeirdThing, f map[string]*Foo, g ...interface{}) (*Result, error)) { ... }
func DoSomethingElse(fn func(a int, b *int, c string, d *SomeWeirdThing, f map[string]*Foo, g ...interface{}) (*Result, error)) { ... }
func DoYetAnotherThing(fn func(a int, b *int, c string, d *SomeWeirdThing, f map[string]*Foo, g ...interface{}) (*Result, error)) { ... }
When you want to attach methods to the type.
In your example, http.HandlerFunc has an attached method, ServeHTTP. This causes the function to satisfy the http.Handler interface. It's only possible to attach methods to named types.
And to answer your related question:
If we want to have a type that exposes a method why don't we create a struct type and add the method to it?
Because there's no reason to do do that. The standard library could have chosen to take your suggestion with:
type HandlerFunc struct {
Func func(ResponseWriter, *Request)
}
But that's far more verbose, and harder to use and read. To use that, you'd then have to call:
http.HandlerFunc{Func: fn}
instead of the much simpler:
http.HandlerFunc(fn)
So there's no reason to add unnecessary complexity.

How to write a pop() function

a := []int{1,2,3}
x, a := a[len(a)-1], a[:len(a)-1]
fmt.Println(a,x)
How to create a pop() function that will do the same for any type of an array?
Here is what I came up with so far:
func pop(a []*interface{}) interface{}{
x := a[len(a)-1]
a = a[:len(a)-1]
return x
}
func main(){
a := []int{1,2,3}
x = pop(a)
fmt.Println(a,x) // -> [1,2] 3
}
But I get cannot use a (type []int) as type []interface {}or other error messages if I try to tweak the code by trial and error.
package main
import (
"fmt"
"reflect"
)
func pop(a interface{}) interface{} {
v := reflect.ValueOf(a).Elem()
x := v.Index(v.Len() - 1)
v.SetLen(v.Len() - 1)
return x
}
func main() {
a := []int{1, 2, 3}
x := pop(&a)
fmt.Println(a, x) // -> [1,2] 3
}
Though this can be implemented, I still think that x, a = a[len(a)-1], a[:len(a)-1] should be better than a pop function.
The go type system doesn't allow you to cast from []type1 -> []type2. Even if it did interfaces are a struct containing a type id and pointer to the object, where normally you would just have the object. Because of this you need to take a interface{} and use reflect to do the slicing.
func pop(slice interface{}) (interface{}, interface{}) {
v := reflect.ValueOf(slice)
return v.Slice(0,v.Len()-1).Interface(), v.Index(v.Len()-1).Interface()
}
Go Playground
Note that this loses compile time type safety, because it must use an interface. Additionally, due to using interfaces the poped value may be allocated, creating extra GC pressure.
Common Go style typically recommends not writing a function like this, and just inlining the small amount of code manually.
After all that really good anwers using reflection I also want to add one answer which offers a more idiomatic Go solution. Like Rob Pike said in his great talk about Go Proverbs
interface{} says nothing
Reflection is never clear
So there should be also one answer showing the idiomatic Go way. This solution does not work for slices of standard types. But there the answer of cshu shows the best solution: x, a = a[len(a)-1], a[:len(a)-1]
For own defined types we have to define a Poper interface and the Pop function takes that as input and returns an empty interface.
type Poper interface {
Pop() interface{}
}
type MyType struct {
a []int
}
func (mt *MyType) Pop() interface{} {
x := mt.a[len(mt.a)-1]
mt.a = mt.a[:len(mt.a)-1]
return x
}
func Pop(p Poper) interface{} {
return p.Pop()
}
func main() {
a := &MyType{[]int{1, 2, 3}}
fmt.Println(Pop(a), a)
}
https://play.golang.org/p/UbDkoVYSMA
At all it is not a good idea to return an empty interface, because all following code has to support the interface{}.
The following code example does not work:
func main() {
a := &MyType{[]int{1, 2, 3}}
fmt.Println(Pop(a), a)
var b int
b = Pop(a)
}
https://play.golang.org/p/wg9__O44A8
The error says everything about that problem: cannot use Pop(a) (type interface {}) as type int in assignment: need type assertion
So the Pop() function does work by returning interface{} but the rest of the code using the result of that function needs to make a type assertion. So if you can avoid it you should search for another solution using types.

Functions type converting

How can I convert func add (a, b int) int to func(...interface{}) interace{} type ?
Any ideas about implementing generic functions using the reflect package ?
As JimB said, you can't cast in Go and you cannot convert functions just like that but by using closures, you can rapidly wrap your function:
func add(a, b int) int {
return a + b;
}
wrap := func(args ...interface{}) interface{} {
return interface{} (add(args[0].(int), args[1].(int)))
}
Note that wrap will panic if you give it arguments that are not of type int. If you want to avoid that you can slightly modify wrap:
wrap := func(args ...interface{}) (interface{}, error) {
a, k := args[0].(int)
b, l := args[1].(int)
if !k || !l {
return nil, errors.New("Arguments must be of type int")
}
return add(a,b), nil
}
If you'd like to do different things with wrap, depending on it's arguments types you can do so by using a type switch:
func addInts(a, b int) int {
return a + b;
}
func addFloat64s(a, b float64) float64 {
return a + b;
}
wrap := func(args ...interface{}) interface{} {
switch args[0].(type) {
case int: return interface{}(addInts(args[0].(int), args[1].(int)))
case float64: return interface{}(addFloat64s(args[0].(float64), args[1].(float64)))
}
}
Note that this last version of wrap makes the assumption that all given parameters will have the same type and at least 2 arguments are given.
There is no "casting" is go (well, using the "unsafe" package kind of is like casting).
You cannot convert function types like this, since they have different layouts in memory. Generic-like functions can be made through the reflect package, though with significant overhead. See http://golang.org/pkg/reflect/#example_MakeFunc for an example.
For most use cases of generic functions, you're probably better off accepting an interface, and using type assertions or switches (http://golang.org/ref/spec#Type_switches), rather than the reflection library.

Resources