Tags: #go
// REFERENCE:
// https://blog.burntsushi.net/type-parametric-functions-golang/
//
package main
import (
"fmt"
"log"
"reflect"
)
func Map(f interface{}, xs interface{}) interface{} {
vf := reflect.ValueOf(f)
vxs := reflect.ValueOf(xs)
ftype := vf.Type()
xstype := vxs.Type()
// 1) Map's first parameter type must be `func(A) B`
if ftype.Kind() != reflect.Func {
log.Panicf("`f` should be %s but got %s", reflect.Func, ftype.Kind())
}
if ftype.NumIn() != 1 {
log.Panicf("`f` should have 1 parameter but it has %d parameters",
ftype.NumIn())
}
if ftype.NumOut() != 1 {
log.Panicf("`f` should return 1 value but it returns %d values",
ftype.NumOut())
}
// 2) Map's second parameter type must be `[]A1` where `A == A1`.
if xstype.Kind() != reflect.Slice {
log.Panicf("`xs` should be %s but got %s", reflect.Slice, xstype.Kind())
}
if xstype.Elem() != ftype.In(0) {
log.Panicf("type of `f`'s parameter should be %s but xs contains %s",
ftype.In(0), xstype.Elem())
}
// 3) Map's return type must be `[]B1` where `B == B1`.
tys := reflect.SliceOf(vf.Type().Out(0))
vys := reflect.MakeSlice(tys, vxs.Len(), vxs.Len())
for i := 0; i < vxs.Len(); i++ {
y := vf.Call([]reflect.Value{vxs.Index(i)})[0]
vys.Index(i).Set(y)
}
return vys.Interface()
}
func main() {
squared := Map(func(x int) int { return x * x }, []int{1, 2, 3}).([]int)
fmt.Printf("%+v\n", squared) // [1 4 9]
squared = Map(func(a string) int { return len(a) }, []int{1, 2, 3}).([]int)
fmt.Printf("%+v\n", squared) // panic: type of `f`'s parameter should be string but xs contains int
}