package main

import (
	"fmt"
	"reflect"
)

type duck interface{}

// Python like map function
func Map(fn duck, list duck) duck {
	fnType, fnValue := reflect.TypeOf(fn), reflect.ValueOf(fn)
	listValue := reflect.ValueOf(list)
	// fmt.Println("assert", fnType.NumOut() == 1, fnType.NumIn() == 1, fnType.In(0) == listType.Elem())
	rtnType := reflect.SliceOf(fnType.Out(0))
	rtnValue := reflect.MakeSlice(rtnType, 0, listValue.Len())
	for i := 0; i < listValue.Len(); i++ {
		in := []reflect.Value{listValue.Index(i)}
		rtnValue = reflect.Append(rtnValue, fnValue.Call(in)[0])
	}
	return rtnValue.Interface()

}

// Python like range function
func Range(n int) []int {
	rtn := make([]int, 0, n)
	for i := 0; i < n; i++ {
		rtn = append(rtn, i)
	}
	return rtn
}

type in chan duck

// Python like list comprehension construction
func List(fn func(x in)) duck {
	x := make(chan duck)
	go func() { fn(x); close(x) }()
	first := <-x
	rtnType := reflect.SliceOf(reflect.TypeOf(first))
	rtnValue := reflect.MakeSlice(rtnType, 0, 0)
	rtnValue = reflect.Append(rtnValue, reflect.ValueOf(first))
	for v := range x {
		rtnValue = reflect.Append(rtnValue, reflect.ValueOf(v))
	}
	return rtnValue.Interface()
}
func main() {
	fn := func(x int) string { return fmt.Sprintf("%dx%d=%d", x, x, x*x) }
        // Compare to rtn = map(fn, range(10))
	rtn := Map(fn, Range(10)).([]string)
	fmt.Println(rtn)

	// Compare to rtn2 = [ i * i for i in range(5)]
	rtn2 := List(func(x in) {for i := 0; i < 5; i++ { x <- i * i }}).([]int)	
	fmt.Println(rtn2)
}