diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res index f005c1fc..51efdbea 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res @@ -12,5 +12,4 @@ open Reducer_TestHelpers describe("Debugging", () => { testEvalToBe("inspect(1)", "Ok(1)") testEvalToBe("inspect(1, \"one\")", "Ok(1)") - testEvalToBe("inspectPerformance(1, \"one\")", "Ok(1)") }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res new file mode 100644 index 00000000..674012be --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res @@ -0,0 +1,11 @@ +open Jest +open Reducer_TestHelpers + +describe("map reduce", () => { + testEvalToBe("double(x)=2*x; arr=[1,2,3]; map(arr, double)", "Ok([2,4,6])") + testEvalToBe("myadd(acc,x)=acc+x; arr=[1,2,3]; reduce(arr, 0, myadd)", "Ok(6)") + testEvalToBe("change(acc,x)=acc*x+x; arr=[1,2,3]; reduce(arr, 0, change)", "Ok(15)") + testEvalToBe("change(acc,x)=acc*x+x; arr=[1,2,3]; reduceReverse(arr, 0, change)", "Ok(9)") + testEvalToBe("arr=[1,2,3]; reverse(arr)", "Ok([3,2,1])") + testEvalToBe("check(x)=(x==2);arr=[1,2,3]; keep(arr,check)", "Ok([2])") +}) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res index 25fc05a7..d1a77040 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res @@ -1,6 +1,9 @@ -module ExternalLibrary = ReducerInterface.ExternalLibrary -module MathJs = Reducer_MathJs module Bindings = Reducer_Expression_Bindings +module ExpressionT = Reducer_Expression_T +module ExternalLibrary = ReducerInterface.ExternalLibrary +module Lambda = Reducer_Expression_Lambda +module MathJs = Reducer_MathJs +module Result = Belt.Result open ReducerInterface.ExpressionValue open Reducer_ErrorValue @@ -12,7 +15,10 @@ open Reducer_ErrorValue exception TestRescriptException -let callInternal = (call: functionCall, _environment): result<'b, errorValue> => { +let callInternal = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result< + 'b, + errorValue, +> => { let callMathJs = (call: functionCall): result<'b, errorValue> => switch call { | ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests @@ -54,21 +60,6 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> => value->Ok } - /* - NOTE: This function is cancelled. The related issue is - https://github.com/webpack/webpack/issues/13435 - */ - let inspectPerformance = (value: expressionValue, label: string) => { - // let _ = %raw("{performance} = require('perf_hooks')") - // let start = %raw(`performance.now()`) - // let finish = %raw(`performance.now()`) - // let performance = finish - start - // Js.log(`${label}: ${value->toString} performance: ${Js.String.make(performance)}ms`) - // TODO find a way of failing the hook gracefully, also needs a block parameter - Js.log(`${label}: ${value->toString}`) - value->Ok - } - let doSetBindings = ( externalBindings: externalBindings, symbol: string, @@ -83,19 +74,65 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> => let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok + let doKeepArray = (aValueArray, aLambdaValue) => { + let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) => + rAcc->Result.flatMap(acc => { + let rNewElem = Lambda.doLambdaCall(aLambdaValue, list{elem}, environment, reducer) + rNewElem->Result.map(newElem => + switch newElem { + | EvBool(true) => list{elem, ...acc} + | _ => acc + } + ) + }) + ) + rMappedList->Result.map(mappedList => mappedList->Belt.List.toArray->EvArray) + } + + let doMapArray = (aValueArray, aLambdaValue) => { + let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) => + rAcc->Result.flatMap(acc => { + let rNewElem = Lambda.doLambdaCall(aLambdaValue, list{elem}, environment, reducer) + rNewElem->Result.map(newElem => list{newElem, ...acc}) + }) + ) + rMappedList->Result.map(mappedList => mappedList->Belt.List.toArray->EvArray) + } + + let doReduceArray = (aValueArray, initialValue, aLambdaValue) => { + aValueArray->Belt.Array.reduce(Ok(initialValue), (rAcc, elem) => + rAcc->Result.flatMap(acc => + Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer) + ) + ) + } + + let doReduceReverseArray = (aValueArray, initialValue, aLambdaValue) => { + aValueArray->Belt.Array.reduceReverse(Ok(initialValue), (rAcc, elem) => + rAcc->Result.flatMap(acc => + Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer) + ) + ) + } + switch call { | ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) => arrayAtIndex(aValueArray, fIndex) | ("$atIndex", [EvRecord(dict), EvArray([EvString(sIndex)])]) => recordAtIndex(dict, sIndex) - | ("$atIndex", [obj, index]) => - (toStringWithType(obj) ++ "??~~~~" ++ toStringWithType(index))->EvString->Ok | ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) - | ("inspect", [value, EvString(label)]) => inspectLabel(value, label) - | ("inspect", [value]) => inspect(value) - | ("inspectPerformance", [value, EvString(label)]) => inspectPerformance(value, label) + | ("$exportBindings", [EvRecord(externalBindings)]) => doExportBindings(externalBindings) | ("$setBindings", [EvRecord(externalBindings), EvSymbol(symbol), value]) => doSetBindings(externalBindings, symbol, value) - | ("$exportBindings", [EvRecord(externalBindings)]) => doExportBindings(externalBindings) + | ("inspect", [value, EvString(label)]) => inspectLabel(value, label) + | ("inspect", [value]) => inspect(value) + | ("keep", [EvArray(aValueArray), EvLambda(aLambdaValue)]) => + doKeepArray(aValueArray, aLambdaValue) + | ("map", [EvArray(aValueArray), EvLambda(aLambdaValue)]) => doMapArray(aValueArray, aLambdaValue) + | ("reduce", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) => + doReduceArray(aValueArray, initialValue, aLambdaValue) + | ("reduceReverse", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) => + doReduceReverseArray(aValueArray, initialValue, aLambdaValue) + | ("reverse", [EvArray(aValueArray)]) => aValueArray->Belt.Array.reverse->EvArray->Ok | call => callMathJs(call) } } @@ -103,12 +140,16 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> => /* Reducer uses Result monad while reducing expressions */ -let dispatch = (call: functionCall, environment): result => +let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result< + expressionValue, + errorValue, +> => try { + let callInternalWithReducer = (call, environment) => callInternal(call, environment, reducer) let (fn, args) = call // There is a bug that prevents string match in patterns // So we have to recreate a copy of the string - ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternal) + ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternalWithReducer) } catch { | Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error | _ => RETodo("unhandled rescript exception")->Error diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index e1df1418..81830e84 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -77,7 +77,8 @@ and reduceValueList = (valueList: list, environment): result< 'e, > => switch valueList { - | list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment) + | list{EvCall(fName), ...args} => + (fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment, reduceExpression) | list{EvLambda(lamdaCall), ...args} => Lambda.doLambdaCall(lamdaCall, args, environment, reduceExpression) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index 55931cc7..35546465 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -55,6 +55,5 @@ let applyParametersToLambda = ( ) } -let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => { +let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => applyParametersToLambda(lambdaValue, args, environment, reducer) -}