-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfunction.go
167 lines (149 loc) · 5.39 KB
/
function.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package monadgo
import (
"reflect"
)
type funcTR struct {
in [2]reflect.Type
out [1]reflect.Type
x reflect.Value
}
func (f funcTR) call(v ...reflect.Value) reflect.Value {
return f.x.Call(v)[0].Interface().(reflect.Value)
}
func (f funcTR) invoke(v interface{}) interface{} {
return f.call(reflect.ValueOf(v)).Interface()
}
func (f funcTR) fold(z, v interface{}) interface{} {
return f.call(reflect.ValueOf(z), reflect.ValueOf(v)).Interface()
}
// funcOf wraps original function f to one-input and one-output function.
// Input may be Unit if no input from f, original input, or tuple binding inputs from f.
// Output may be Unit if no output from f, Null if nil returns, or tuple binding outpus from f.
func funcOf(f interface{}) funcTR {
ftyp := reflect.TypeOf(f)
fval := reflect.ValueOf(f)
switch ftyp.NumIn() {
case 0: // input unit
resultF := reflect.FuncOf([]reflect.Type{typeAny}, typeValues, false)
return funcTR{
in: [2]reflect.Type{typeAny},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
out := reflect.ValueOf(bindValues(ftyp, fval.Call(nil)))
return []reflect.Value{reflect.ValueOf(out)}
})}
case 1: // input original value from f.
resultF := reflect.FuncOf([]reflect.Type{ftyp.In(0)}, typeValues, false)
return funcTR{
in: [2]reflect.Type{ftyp.In(0)},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
out := reflect.ValueOf(bindValues(ftyp, fval.Call(args)))
return []reflect.Value{reflect.ValueOf(out)}
})}
default: // bind all input from f to tuple.
resultF := reflect.FuncOf([]reflect.Type{typeTuple}, typeValues, false)
return funcTR{
in: [2]reflect.Type{typeTuple},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
t := args[0].Interface().(Tuple)
out := reflect.ValueOf(bindValues(ftyp, fval.Call(t.toValues())))
return []reflect.Value{reflect.ValueOf(out)}
})}
}
}
// foldOf wraps original function f to two-input and one-output function.
// Output may be Unit if no output from f, Null if nil returns, or tuple binding outpus from f.
func foldOf(f interface{}) funcTR {
ftyp := reflect.TypeOf(f)
if ftyp.NumIn() < 1 {
panic("fold function must have one argument at last.")
}
fval := reflect.ValueOf(f)
switch ftyp.NumIn() {
case 1: // input (z) from f and unit.
resultF := reflect.FuncOf([]reflect.Type{ftyp.In(0), typeAny}, typeValues, false)
return funcTR{
in: [2]reflect.Type{ftyp.In(0), typeAny},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
out := reflect.ValueOf(bindValues(ftyp, fval.Call(args[0:1])))
return []reflect.Value{reflect.ValueOf(out)}
})}
case 2: // inputs (z, x) from f.
resultF := reflect.FuncOf([]reflect.Type{ftyp.In(0), ftyp.In(1)}, typeValues, false)
return funcTR{
in: [2]reflect.Type{ftyp.In(0), ftyp.In(1)},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
out := reflect.ValueOf(bindValues(ftyp, fval.Call(args)))
return []reflect.Value{reflect.ValueOf(out)}
})}
default:
if ftyp.NumIn()&1 == 1 {
// num of input is odd.
resultF := reflect.FuncOf([]reflect.Type{ftyp.In(0), typeTuple}, typeValues, false)
return funcTR{
in: [2]reflect.Type{ftyp.In(0), typeTuple},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
t := args[1].Interface().(Tuple)
vals := append(args[0:1], t.toValues()...)
out := reflect.ValueOf(bindValues(ftyp, fval.Call(vals)))
return []reflect.Value{reflect.ValueOf(out)}
})}
}
// num of input is even.
resultF := reflect.FuncOf([]reflect.Type{typeTuple, typeTuple}, typeValues, false)
return funcTR{
in: [2]reflect.Type{typeTuple, typeTuple},
out: [1]reflect.Type{bindType(ftyp)},
x: reflect.MakeFunc(resultF, func(args []reflect.Value) []reflect.Value {
t0 := args[0].Interface().(Tuple)
t1 := args[1].Interface().(Tuple)
vals := append(t0.toValues(), t1.toValues()...)
out := reflect.ValueOf(bindValues(ftyp, fval.Call(vals)))
return []reflect.Value{reflect.ValueOf(out)}
})}
}
}
// ----------------------------------------------------------------------------
// bindValues converts []reflect.Value to callable type.
// return Unit if output size is 0, native if size is 1, and Tuple if size more than 1.
func bindValues(ftyp reflect.Type, outs []reflect.Value) interface{} {
switch len(outs) {
case 0:
return unit
case 1:
if outs[0].Interface() == nil {
return null
}
return outs[0].Interface()
case 2:
return pairFromTuple2(newTuple2(ftyp.Out(0), ftyp.Out(1), outs[0], outs[1]))
case 3:
return newTuple3(ftyp.Out(0), ftyp.Out(1), ftyp.Out(2), outs[0], outs[1], outs[2])
case 4:
return newTuple4(ftyp.Out(0), ftyp.Out(1), ftyp.Out(2), ftyp.Out(3), outs[0], outs[1], outs[2], outs[3])
default:
types := make([]reflect.Type, ftyp.NumOut(), ftyp.NumOut())
for i := 0; i < ftyp.NumOut(); i++ {
types[i] = ftyp.Out(i)
}
return newTuple(types, outs)
}
}
func bindType(ftyp reflect.Type) reflect.Type {
n := ftyp.NumOut()
switch ftyp.NumOut() {
case 0:
return typeUnit
case 1:
return ftyp.Out(0)
case 2:
return typePair
default:
return typeTuples[n-2]
}
}