reflection - Idiomatic way to implement generic functions in Go -
let's want write function check whether predicate matched element in slice:
func isin(array []t, pred func(elt t) bool) bool { _, obj := range array { if pred(obj) { return true;} } return false; }
obviously, previous code won't compile, since t
not exist. can replace interface{}
this:
func isin(array[]interface{}, pred func(elt interface{}) bool) bool { ... }
as happy let predicate perform casting:
isin([]interface{}{1,2,3,4}, func(o interface{}) {return o.(int) == 3; });
but then, function won't accept array not of type []interface{}
:
isin([]int{1,2,3,4}, func(o interface{}) { return o.(int) == 3; }) // not compile
and similarly:
func isin(arr interface, pred func(o interface{}) bool) bool { _, o := range arr.([]interface{}) { ... } } isin([]int{1,2,3,4}, func(o interface{}) { return o.(int) == 3; }) // panics @ runtime (cannot cast []int []interface)
the other alternative have typed functions each array type:
isinint(arr []int, pred func(i int) bool) { ... } isinstr(arr []string, pred func(s string) bool) { ... } ...
but seems lot of code duplication.
has come nice way deal such situations ?
edit
thanks jnml's fantastic tips on go reflection, think have found nice way express these patterns, converting every 'iterable' channel:
func iter(obj interface{}) chan interface{} { c := make(chan interface{}) v := reflect.valueof(obj) if (v.kind() == reflect.array || v.kind() == reflect.slice) { go func() { := 0; < v.len(); i++ { c<-v.index(i).interface() } close(c) }() } else if v.kind() == reflect.chan { go func() { x, ok := v.recv() ok { c<-x.interface() x,ok = v.recv() } close(c) }() } else if (... whatever iteration protocol have ...) { } else { panic("cannot iterate !") } return c; }
with initial example rewritten using on playground.
for example:
package main import ( "fmt" "reflect" ) func checkslice(slice interface{}, predicate func(reflect.value) bool) bool { v := reflect.valueof(slice) if v.kind() != reflect.slice { panic("not slice") } := 0; < v.len(); i++ { if predicate(v.index(i)) { return true } } return false } func main() { := []int{1, 2, 3, 4, 42, 278, 314} fmt.println(checkslice(a, func(v reflect.value) bool { return v.int() == 42 })) b := []float64{1.2, 3.4, -2.5} fmt.println(checkslice(b, func(v reflect.value) bool { return v.float() > 4 })) }
output:
true false
Comments
Post a Comment