diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res index f376094a..398530e3 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res @@ -17,6 +17,10 @@ describe("builtin", () => { testEval("1-1", "Ok(0)") testEval("2>1", "Ok(true)") testEval("concat('a','b')", "Ok('ab')") + testEval( + "addOne(t)=t+1; toInternalSampleArray(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))", + "Ok([2,3,4,5,6,7])", + ) }) describe("builtin exception", () => { diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 2bb409ad..5bba9f1d 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -37,6 +37,7 @@ module Error = { | LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}` | SampleSetError(TooFewSamples) => "Too Few Samples" | SampleSetError(NonNumericInput(err)) => `Found a non-number in input: ${err}` + | SampleSetError(OperationError(err)) => Operation.Error.toString(err) | OperationError(err) => Operation.Error.toString(err) | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err) | SparklineError(err) => PointSetTypes.sparklineErrorToString(err) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res index 55b334c5..834008f5 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist.res @@ -1,12 +1,14 @@ @genType module Error = { @genType - type sampleSetError = TooFewSamples | NonNumericInput(string) + type sampleSetError = + TooFewSamples | NonNumericInput(string) | OperationError(Operation.operationError) let sampleSetErrorToString = (err: sampleSetError): string => switch err { | TooFewSamples => "Too few samples when constructing sample set" | NonNumericInput(err) => `Found a non-number in input: ${err}` + | OperationError(err) => Operation.Error.toString(err) } @genType @@ -16,6 +18,8 @@ module Error = { switch err { | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set" } + + let fromOperationError = e => OperationError(e) } include Error @@ -83,6 +87,14 @@ let sampleN = (t: t, n) => { } } +let samplesMap = (~fn: float => result, t: t): result< + t, + sampleSetError, +> => { + let samples = T.get(t)->E.A2.fmap(fn) + E.A.R.firstErrorOrOpen(samples)->E.R2.errMap(Error.fromOperationError) |> E.R2.bind(make) +} + //TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this. let map2 = (~fn: (float, float) => result, ~t1: t, ~t2: t): result< t, @@ -96,7 +108,7 @@ let map2 = (~fn: (float, float) => result, ~t1: t, ~t2 // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array} // But doing so would take too much time, so I'll leave it as an assertion E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x => - E.R.toExn("Input of samples should be larger than 5", make(x)) + E.R.toExnFnString(Error.sampleSetErrorToString, make(x)) ) } 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 d1a77040..cd16e1fe 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 @@ -99,6 +99,18 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce rMappedList->Result.map(mappedList => mappedList->Belt.List.toArray->EvArray) } + let doMapSampleSetDist = (sampleSetDist: SampleSetDist.t, aLambdaValue) => { + let fn = r => + switch Lambda.doLambdaCall(aLambdaValue, list{EvNumber(r)}, environment, reducer) { + | Ok(EvNumber(f)) => Ok(f) + | _ => Error(Operation.SampleMapNeedsNtoNFunction) + } + switch SampleSetDist.samplesMap(~fn, sampleSetDist) { + | Ok(r) => Ok(EvDistribution(SampleSet(r))) + | Error(r) => Error(REDistributionError(SampleSetError(r))) + } + } + let doReduceArray = (aValueArray, initialValue, aLambdaValue) => { aValueArray->Belt.Array.reduce(Ok(initialValue), (rAcc, elem) => rAcc->Result.flatMap(acc => @@ -128,6 +140,8 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce | ("keep", [EvArray(aValueArray), EvLambda(aLambdaValue)]) => doKeepArray(aValueArray, aLambdaValue) | ("map", [EvArray(aValueArray), EvLambda(aLambdaValue)]) => doMapArray(aValueArray, aLambdaValue) + | ("mapSamples", [EvDistribution(SampleSet(dist)), EvLambda(aLambdaValue)]) => + doMapSampleSetDist(dist, aLambdaValue) | ("reduce", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) => doReduceArray(aValueArray, initialValue, aLambdaValue) | ("reduceReverse", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) => diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 4d859a79..d36ca5c4 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -4,6 +4,7 @@ type errorValue = | REArrayIndexNotFound(string, int) | REAssignmentExpected | REDistributionError(DistributionTypes.error) + | REOperationError(Operation.operationError) | REExpressionExpected | REFunctionExpected(string) | REJavaScriptExn(option, option) // Javascript Exception @@ -29,6 +30,7 @@ let errorToString = err => | REExpressionExpected => "Expression expected" | REFunctionExpected(msg) => `Function expected: ${msg}` | REDistributionError(err) => `Distribution Math Error: ${DistributionTypes.Error.toString(err)}` + | REOperationError(err) => `Math Error: ${Operation.Error.toString(err)}` | REJavaScriptExn(omsg, oname) => { let answer = "JS Exception:" let answer = switch oname { diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index 0f29f9db..8366818a 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -306,6 +306,8 @@ let dispatchToGenericOutput = ( Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist, ~env) | ("toSampleSet", [EvDistribution(dist)]) => Helpers.toDistFn(ToSampleSet(env.sampleCount), dist, ~env) + | ("toInternalSampleArray", [EvDistribution(SampleSet(dist))]) => + Some(FloatArray(SampleSetDist.T.get(dist))) | ("fromSamples", [EvArray(inputArray)]) => { let _wrapInputErrors = x => SampleSetDist.NonNumericInput(x) let parsedArray = Helpers.parseNumberArray(inputArray)->E.R2.errMap(_wrapInputErrors) diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index 64729324..b08754ef 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -235,13 +235,16 @@ module R = { | Ok(a) => f(a) | Error(err) => Error(err) } - let toExn = (msg: string, x: result<'a, 'b>): 'a => switch x { | Ok(r) => r | Error(_) => raise(Assertion(msg)) } - + let toExnFnString = (errorToStringFn, o) => + switch o { + | Ok(r) => r + | Error(r) => raise(Assertion(errorToStringFn(r))) + } let default = (default, res: Belt.Result.t<'a, 'b>) => switch res { | Ok(r) => r diff --git a/packages/squiggle-lang/src/rescript/Utility/Operation.res b/packages/squiggle-lang/src/rescript/Utility/Operation.res index 3f56493b..7972b2fa 100644 --- a/packages/squiggle-lang/src/rescript/Utility/Operation.res +++ b/packages/squiggle-lang/src/rescript/Utility/Operation.res @@ -55,6 +55,7 @@ type operationError = | ComplexNumberError | InfinityError | NegativeInfinityError + | SampleMapNeedsNtoNFunction | PdfInvalidError | NotYetImplemented // should be removed when `klDivergence` for mixed and discrete is implemented. @@ -69,6 +70,7 @@ module Error = { | ComplexNumberError => "Operation returned complex result" | InfinityError => "Operation returned positive infinity" | NegativeInfinityError => "Operation returned negative infinity" + | SampleMapNeedsNtoNFunction => "SampleMap needs a function that converts a number to a number" | PdfInvalidError => "This Pdf is invalid" | NotYetImplemented => "This pathway is not yet implemented" }