golang: trying to use `any` to match different functions - go

Given this function:
func Apply(label string, func(msg any, arg any) (any, error) {
...
}
Unfortunately the funcs I need to pass to Apply don't match in their signature.
//Func 1
func GetAddress(prompt string, custom Param) (string, error) {}
// Func 2
func GetIdentifier(prompt string) (utils.ID, error) {}
But why does the compiler (go1.18) complain if I try
Apply(label, GetAddress)
with
[compiler IncompatibleAssign] [E] cannot use GetAddress (value of
type func(prompt string, custom Param) (string, error)) as
func(msg any, arg any) (any, error) value in argument to Apply
Why does msg any not match to prompt string, custom Param not to arg any and the string type in the return not to the any type in the return?
What exactly is incompatible here? (Ignore if you think the design is bad for a sec, I just want to understand that first).

Change the func declaration to use type parameters
func Apply[T1 any, T2 any, T3 any](label string, func(msg T1, arg T2) (T3, error)
Now to call it
Apply[string, Param, string](label, GetAddress)
When the function has type parameters, you may be able to drop the type parameter list with the compiler automatically inferring what the variant types are:
Apply(label, GetAddress)

The reason why the code you wrote doesn't actually work is that the types have to match exactly in the go language. The interface type is valid for incoming value types, the definition types must be the same in function, however, if the definition type is interface (or "any"), the parameter value can be any. (string|int ...)
func Apply(label string, func(msg any, arg any) (any, error) {
...
}
//Func 1
func GetAddress(prompt any, custom any) (any, error) {}
// Func 2
func GetIdentifier(prompt any) (any, error) {}

func Apply[K any](label string, func(msg K, arg K) (K, error) {
...
}
maybe something like this could work
Apply[K any]

Related

Passing an arbitrary function as a parameter in 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.

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)

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.

Golang pass nil as optional argument to a function?

In Golang, http.NewRequest has a specification like this:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
However, I can pass nil as the body option if I don't want to pass the body to an io.Reader object, like this:
req, err := http.NewRequest("GET", "http://www.blahblah.org", nil)
How do I implement this functionality in my code? I have a function that I want to pass an optional string value so that it can page through API results however if I pass a nil to the string input I get this:
./snippets.go:32: cannot convert nil to type string
The parameters for my function look like this:
func getChallenges(after string) ([]challenge, string, error)
Go does not have "optional" arguments as a generally understood concept in other languages; nil is just the zero value for an interface (io.Reader in this case).
The equivalent zero value for a string is an empty string:
getChallenges("")
If you want to accept 0 or more of the same argument type, you use the variadic syntax:
func getChallenges(after ...string) ([]challenge, string, error)
You can modify you function to receive pointer value, like this:
func getChallenges(after *string) ([]challenge, string, error)
Then you can pass nil as an argument to it. But don't forget to check after for nil value inside your function before dereferencing it, or you will get a nil pointer exception:
func getChallenges(after *string) ([]challenge, string, error) {
if after == nil {
// No value specified
} else {
fmt.Printf("After: %s\n", *after) // Note pointer dereferencing with "*"
}
// ...
}
Another option:
Just use two functions:
func getChallenges(after string) {}
func getAllChallenges() {
return getChallenges(/* some default value here */)
}
you can use ellipse operator to send the optional parameters.. don't pass anything in optional parameter and check the length of parameter.
it should solve your problem
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
Maybe wrap it in a struct?
type NilableString struct {
value string;
}
You can use reflect.
In fact io.Reader is a interface.
So you can define signature like func getChallenges(after interface{}) ([]challenge, string, error)
interface{} is a empty interface, that is interface for anything.
But I suggest you use syntax args... to pass slice , refer to fmt.Printf implementation for usage, because if you pass no string the slice len is 0 and
this will avoid the reflect which I think too heavy for your function.

Writing a generic binder in 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.

Resources