hi there,
I was trying out the generics proposal
https://github.com/golang/go/issues/15292, via the "Go2 playground":
https://ccbrown.github.io/wasm-go-playground/experimental/generics
the thing I am trying to solve is, basically, a generic way to:
- pass a function with any number of parameters and a single return value
- "bind" that function to a slice of pointers to values (that will point to, e.g., values extracted from a db)
- create a closure that returns the value from evaluating the function with the bound paramaters.
basically, I am defining this interface:
type Formula interface {
Vars() []string
Bind(ptrs []interface{}) error
Func() interface{}
}
that people would be able (in Go-1) to implement like so:
type FormF64F32ToI64 struct {
vars []string
arg0 *float64
arg1 *float32
fct func(x1 float64, x2 float32) int64
}
func (f *FormF64F32ToI64) Vars() []string { return f.vars }
func (f *FormF64F32ToI64) Bind(ptrs []interface{}) error {
// check number of args elided.
f.arg0 = ptrs[0].(*float64)
f.arg1 = ptrs[1].(*float32)
return nil
}
func (f *FormF64F32ToI64) Func() interface{} {
return func() int64 {
return f.fct(*f.arg0, *f.arg1)
}
}
so some "framework/library" code can work in terms of this Formula interface and let clients register formulae (with a set of column names to read from the database) in a relatively type-safe way, and quite performant one too (you pay the price of the type-assert once when you retrieve the function closure):
usr, err := db.Formula(&FormF64F32ToI64{
names: []string{"col1", "col42"},
fct: func(x1 float64, x2 float32) int64 { return int64(x1) + int64(x2) },
})
check(err)
fct := usr.Func().(func () int64)
err = db.IterRows(func(tx Tx)error {
fmt.Printf("row[%d]: %d\n", tx.ID, fct())
})
check(err)
implementing the Formula interface from a given func is a pretty mechanical job.
right now, I am doing it with a little go/types-based command.
today, I tried to do the same with the generics proposal:
=====
package main
type Formula interface {
Names() []string
Bind(args []interface{}) error
Func() interface{}
}
type Form2 (type T1,T2,U) struct {
names []string
arg1 *T1
arg2 *T2
fct func(t1 T1, t2 T2) U
}
func (f *Form2(T1,T2,U)) Func() interface{} {
return func() U {
return f.fct(*f.arg1, *f.arg2)
}
}
func (f *Form2(T1,T2,U)) Names() []string { return f.names }
func (f *Form2(T1,T2,U)) Bind(args []interface{}) error {
if len(args) != 2 {
panic("invalid number of arguments")
}
f.arg1 = args[0].(*T1)
f.arg2 = args[1].(*T2)
return nil
}
var (
_ Formula = (*Form2(float64,float32,int64))(nil)
)
func main() {
arg1 := 42.0
arg2 := float32(42)
args := []interface{}{&arg1, &arg2}
form := &Form2(float64,float32,int64){
names: []string{"f64", "data"},
fct: func(x1 float64, x2 float32) int64 { return int64(x1) + int64(x2) },
}
err := form.Bind(args)
if err != nil { panic(err) }
fct := form.Func().(func() int64)
println("fct=",fct())
}
====
it's definitely an improvement: I "just" have to write the generics code for functions with a given arity (say, 0, 1, ... 5) w/o having to pre-generate all the functions I (or my users) may need for all combinations of (types x arity).
it's not completely satisfactory, though, as:
- it's not "as general" a solution as what one could come up with, say, std::function (from C++), where you "just" have to give a function pointer and it handles everything else for you (arity, in this case)
- there's probably the issue of "generic type argument" collision:
how to differentiate (in the current scheme) between 'func(x1 float64) (float64, int64)' and 'func(x1, x2 float64) int64'?
(this could perhaps be addressed w/ a different way of describing my Form2 generic type.)
perhaps there's a better way of doing this? (perhaps even w/ "just" Go1)
it'd be great to just have to write:
var f Formula = &Form(func(float64,float64)int64){
names: names,
fct: func(x1,x2 float64) float32 { return float32(x1+x2) },
}
and have everything instantiated and implemented correctly.
thoughts?
cheers,
-s
PS: here's the direct link to the Go+Generics playground of the code above:
https://ccbrown.github.io/wasm-go-playground/experimental/generics/#A4Qwxg1iDmCmAEBbEBLAdgKAwem/AGoQFzwAqAFigM7zXyXTkA2AnvLAB7CwBOKisNABcQTAHTwAIgHs0AciHsusMIrDSePFULSwqNDfCoiARiiYohLMVivd4AMQ2IArkxC1hvAGbgEAbwx4YPgAORABKgAKAEp4AG0AXWM+NGggkIAhdAATKJAeaBok9CEfP38AXzjeHg0M4IcXNDBYzzKeXzBYKoxK2xZ7Jx5EACZ4KLsEUgBGABpSUbmAVTiUl1V4QJD4NAi9BOShVPSdgugZ+AAqWYb4c/Gb0bvvTe9m1qFL2bn4IXHFnFln0sO8WhNvNdhmMoj9FisYnEmi02qVyt0qlsMABILRCFw8NDwMGtIFY7G42D4wnEsSvIRRK7eMTnebXZkPGI4/r9DAkiFQ5yjWHzeGrOLhSJtJIpdDQLbwPEEonMvaReD9flRSFXaHCuFLcXwbJoPLnYqJNGdCrVdiaQzbEIoSFMQT5QpUOIAQgAvPBxo6dsFQGgUK0AEToABuohQOV2LkQJl48GkkPOicEQio4a5O36Ow5hUufvN8QADIkxIzZnmQkXoONSx74jMqzXRnXgkqaaGmCCMDGeBM7gB9RzONweP2MvXapjSEBCABsABY5t4F0uAMxLUprxFRPtcrl8j5IVBoNqBbGs+BEP2r0Zics4h73v2bxdCXdRJ9c28PQ/Q4rS6HpKn8AAyVlfmgwpRn6bEcW8ZxgMgucvyXNcNy3H892EA8bx2NU9BIGVjjlfxw28Ndw1+cMciXEBw0qOYcULVQSBJKIOEuTCV3XeAOHGfjdzifdVwVHsiQkniZjiABqdo1x4zsNTY7FENqYCUJGMQTTND0AOdO1h19XZzAVEMwyiWo4kQ5DNgfYlnDEZFSWrbjxII1cAOAVIhCYK9qNUH06PpWIuX6IA==