squiggle/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda.res
Vyacheslav Matyukhin a764f3075c
WIP (broken)
2022-10-02 14:04:45 +04:00

81 lines
2.7 KiB
Plaintext

type t = Reducer_T.lambdaValue
// user-defined functions, i.e. `add2 = {|x, y| x + y}`, are built by this method
let makeLambda = (
name: option<string>,
parameters: array<string>,
bindings: Reducer_T.bindings,
body: Reducer_T.expression,
location: Reducer_Peggy_Parse.location,
): t => {
let lambda = (
arguments: array<Reducer_T.value>,
context: Reducer_T.context,
reducer: Reducer_T.reducerFn,
) => {
let argsLength = arguments->E.A.length
let parametersLength = parameters->E.A.length
if argsLength !== parametersLength {
SqError.Message.REArityError(None, parametersLength, argsLength)->SqError.Message.throw
}
// create new bindings scope - technically not necessary, since bindings are immutable, but might help with debugging/new features in the future
let localBindings = bindings->Reducer_Bindings.extend
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
currentBindings,
parameter,
index,
) => {
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
})
let lambdaContext: Reducer_T.context = {
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
environment: context.environment, // environment at the moment when lambda is called
callStack: context.callStack, // extended by main `evaluate` function
}
let (value, _) = reducer(body, lambdaContext)
value
}
FnLambda({
// context: bindings,
name: name,
body: lambda,
parameters: parameters,
location: location,
})
}
// stdlib lambdas (everything in FunctionRegistry) is built by this method. Body is generated in SquiggleLibrary_StdLib.res
let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
// Note: current bindings could be accidentally exposed here through context (compare with native lambda implementation above, where we override them with local bindings).
// But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident.
body: body,
name: name,
})
// this function doesn't scale to FunctionRegistry's polymorphic functions
let parameters = (t: t): array<string> => {
switch t {
| FnLambda({parameters}) => parameters
| FnBuiltin(_) => ["..."]
}
}
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
let newContext = {
...context,
callStack: t->Reducer_CallStack.extend(t),
}
SqError.rethrowWithStacktrace(() => {
switch t {
| FnLambda({body}) => body(args, newContext, reducer)
| FnBuiltin({body}) => body(args, newContext, reducer)
}
}, newContext.callStack)
}