|
1 | 1 | package types
|
2 | 2 |
|
3 |
| -import "context" |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + |
| 6 | + "github.com/pkg/errors" |
| 7 | +) |
4 | 8 |
|
5 | 9 | // Lambda can be applicable, and it has an expression to execute.
|
6 |
| -type Lambda Value |
| 10 | +type Lambda interface { |
| 11 | + Value |
| 12 | + |
| 13 | + // Call calls this lambda with given args |
| 14 | + Call(ctx context.Context, args ...Value) (Value, func(context.Context), error) |
| 15 | +} |
| 16 | + |
| 17 | +type argT Array |
| 18 | + |
| 19 | +// ArgsDef is passed to builder function, the argument of NewLambda() |
| 20 | +type ArgsDef struct { |
| 21 | + args []argT |
| 22 | +} |
| 23 | + |
| 24 | +// Define returns placeholder expression of given argument |
| 25 | +func (def *ArgsDef) Define(n int, name String, typ Type) (Value, error) { |
| 26 | + if n <= 0 { |
| 27 | + return nil, errors.New("the number of argument must be positive") |
| 28 | + } |
| 29 | + for n > len(def.args) { |
| 30 | + def.args = append(def.args, nil) |
| 31 | + } |
| 32 | + if def.args[n-1] != nil { |
| 33 | + return nil, errors.Errorf("the %dth argument is already taken", n) |
| 34 | + } |
| 35 | + argExpr := []Value{NewString("arg"), name} |
| 36 | + def.args[n-1] = NewArray(argExpr, AnyValue) |
| 37 | + return def.args[n-1], nil |
| 38 | +} |
| 39 | + |
| 40 | +// Inject replaces expr of ["arg", expr] with given values |
| 41 | +func (def *ArgsDef) Inject(args []Value) error { |
| 42 | + if len(args) != len(def.args) { |
| 43 | + return errors.Errorf("expected %d arity but got %d", len(def.args), len(args)) |
| 44 | + } |
| 45 | + for i := range args { |
| 46 | + if def.args[i] == nil { |
| 47 | + return errors.Errorf("%dth arg is not taken", i+1) |
| 48 | + } |
| 49 | + def.args[i].Value()[1] = args[i] |
| 50 | + } |
| 51 | + return nil |
| 52 | +} |
7 | 53 |
|
8 | 54 | // NewLambda creates lambda value.
|
9 | 55 | // Signature must have 1 type at least for a return type.
|
10 |
| -func NewLambda(t Type, rest ...Type) Lambda { |
11 |
| - return &lambdaT{typ: NewLambdaType(t, rest...)} |
| 56 | +func NewLambda(builder func(*ArgsDef) (Expr, []Type, error)) (Lambda, error) { |
| 57 | + def := &ArgsDef{args: make([]argT, 0)} |
| 58 | + expr, sig, err := builder(def) |
| 59 | + if err != nil { |
| 60 | + return nil, errors.Wrap(err, "builder function returned an error") |
| 61 | + } |
| 62 | + return &lambdaT{ |
| 63 | + def: def, |
| 64 | + expr: expr, |
| 65 | + typ: NewLambdaType(sig[0], sig[1:]...), |
| 66 | + }, nil |
12 | 67 | }
|
13 | 68 |
|
14 | 69 | type lambdaT struct {
|
15 |
| - typ Type |
| 70 | + def *ArgsDef |
| 71 | + expr Expr |
| 72 | + typ Type |
| 73 | +} |
| 74 | + |
| 75 | +func (v *lambdaT) Call(ctx context.Context, args ...Value) (Value, func(context.Context), error) { |
| 76 | + if err := v.def.Inject(args); err != nil { |
| 77 | + return nil, nil, err |
| 78 | + } |
| 79 | + return v.expr.Eval(ctx) |
16 | 80 | }
|
17 | 81 |
|
18 | 82 | func (v *lambdaT) Invert(context.Context) (Value, error) {
|
|
0 commit comments