From afd14811777ea7a1672813a5dd2a1c2695d42f78 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 18 Jan 2022 11:10:06 +1100 Subject: [PATCH] Add multiple exported graphs (broken) --- bsconfig.json | 2 +- package.json | 1 + src/ExampleStyles.bs.js | 10 - src/components/DistBuilder.re | 345 ------------------------------ src/components/DistBuilder.res | 271 +++++++++++++++++++++++ src/distPlus/ProgramEvaluator.re | 180 ---------------- src/distPlus/ProgramEvaluator.res | 171 +++++++++++++++ yarn.lock | 5 + 8 files changed, 449 insertions(+), 536 deletions(-) delete mode 100644 src/ExampleStyles.bs.js delete mode 100644 src/components/DistBuilder.re create mode 100644 src/components/DistBuilder.res delete mode 100644 src/distPlus/ProgramEvaluator.re create mode 100644 src/distPlus/ProgramEvaluator.res diff --git a/bsconfig.json b/bsconfig.json index a5e2bd0b..49c793ee 100644 --- a/bsconfig.json +++ b/bsconfig.json @@ -37,7 +37,7 @@ "@glennsl/bs-json", "@foretold/components", "bs-ant-design-alt", - "reason-react", + "@rescript/react", "bs-reform", "bs-css", "rationale", diff --git a/package.json b/package.json index 536ad72b..e7fd1eb9 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": { "@foretold/components": "./foretold/components", "@glennsl/bs-json": "^5.0.2", + "@rescript/react": "^0.10.3", "ace-builds": "^1.4.12", "antd": "3.17.0", "autoprefixer": "9.7.4", diff --git a/src/ExampleStyles.bs.js b/src/ExampleStyles.bs.js deleted file mode 100644 index c3ac172f..00000000 --- a/src/ExampleStyles.bs.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - - -var reasonReactBlue = "#48a9dc"; - -var style = "\n body {\n background-color: rgb(224, 226, 229);\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n button {\n background-color: white;\n color: " + reasonReactBlue + ";\n box-shadow: 0 0 0 1px " + reasonReactBlue + ";\n border: none;\n padding: 8px;\n font-size: 16px;\n }\n button:active {\n background-color: " + reasonReactBlue + ";\n color: white;\n }\n .container {\n margin: 12px 0px;\n box-shadow: 0px 4px 16px rgb(200, 200, 200);\n width: 720px;\n border-radius: 12px;\n font-family: sans-serif;\n }\n .containerTitle {\n background-color: rgb(242, 243, 245);\n border-radius: 12px 12px 0px 0px;\n padding: 12px;\n font-weight: bold;\n }\n .containerContent {\n background-color: white;\n padding: 16px;\n border-radius: 0px 0px 12px 12px;\n }\n"; - -exports.reasonReactBlue = reasonReactBlue; -exports.style = style; -/* No side effect */ diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re deleted file mode 100644 index bcbab8e0..00000000 --- a/src/components/DistBuilder.re +++ /dev/null @@ -1,345 +0,0 @@ -open ReForm; -open Antd.Grid; - -module FormConfig = [%lenses - type state = { - squiggleString: string, - sampleCount: string, - outputXYPoints: string, - downsampleTo: string, - kernelWidth: string, - diagramStart: string, - diagramStop: string, - diagramCount: string, - } -]; - -type options = { - sampleCount: int, - outputXYPoints: int, - downsampleTo: option(int), - kernelWidth: option(float), - diagramStart: float, - diagramStop: float, - diagramCount: int, -}; - -module Form = ReForm.Make(FormConfig); - -module FieldText = { - [@react.component] - let make = (~field, ~label) => { - <> - - handleChange(r)} /> - } - /> - ; - }; -}; -module FieldString = { - [@react.component] - let make = (~field, ~label) => { - - R.ste}> - validate()} - /> - - } - />; - }; -}; - -module FieldFloat = { - [@react.component] - let make = (~field, ~label, ~className=Css.style([])) => { - - R.ste}> - validate()} - className - /> - - } - />; - }; -}; - -module Styles = { - open Css; - let rows = - style([ - selector( - ">.ant-col:first-child", - [paddingLeft(em(0.25)), paddingRight(em(0.125))], - ), - selector( - ">.ant-col:last-child", - [paddingLeft(em(0.125)), paddingRight(em(0.25))], - ), - selector( - ">.ant-col:not(:first-child):not(:last-child)", - [paddingLeft(em(0.125)), paddingRight(em(0.125))], - ), - ]); - let parent = - style([ - selector(".ant-input-number", [width(`percent(100.))]), - selector(".anticon", [verticalAlign(`zero)]), - ]); - let form = style([backgroundColor(hex("eee")), padding(em(1.))]); - let dist = style([padding(em(1.))]); - let spacer = style([marginTop(em(1.))]); - let groupA = - style([ - selector( - ".ant-input-number-input", - [backgroundColor(hex("fff7db"))], - ), - ]); - let groupB = - style([ - selector( - ".ant-input-number-input", - [backgroundColor(hex("eaf4ff"))], - ), - ]); -}; - -module DemoDist = { - [@react.component] - let make = (~squiggleString:string, ~options) => { - R.ste}> -
- {switch (options) { - | Some(options) => - let inputs1 = - ProgramEvaluator.Inputs.make( - ~samplingInputs={ - sampleCount: Some(options.sampleCount), - outputXYPoints: Some(options.outputXYPoints), - kernelWidth: options.kernelWidth, - shapeLength: - Some(options.downsampleTo |> E.O.default(1000)), - }, - ~squiggleString, - ~environment= - [| - ("K", `SymbolicDist(`Float(1000.0))), - ("M", `SymbolicDist(`Float(1000000.0))), - ("B", `SymbolicDist(`Float(1000000000.0))), - ("T", `SymbolicDist(`Float(1000000000000.0))), - |] - ->Belt.Map.String.fromArray, - (), - ); - - let response1 = ProgramEvaluator.evaluateProgram(inputs1); - switch (response1) { - | Ok(`DistPlus(distPlus1)) => - - | Ok(`Float(f)) => - - | Ok(`Function((f, a), env)) => - // Problem: When it gets the function, it doesn't save state about previous commands - let foo: ProgramEvaluator.Inputs.inputs = { - squiggleString, - samplingInputs: inputs1.samplingInputs, - environment: env, - }; - let results = - E.A.Floats.range(options.diagramStart, options.diagramStop, options.diagramCount) - |> E.A.fmap(r => - ProgramEvaluator.evaluateFunction( - foo, - (f, a), - [|`SymbolicDist(`Float(r))|], - ) - |> E.R.bind(_, a => - switch (a) { - | `DistPlus(d) => Ok((r, DistPlus.T.normalize(d))) - | n => - Js.log2("Error here", n); - Error("wrong type"); - } - ) - ) - |> E.A.R.firstErrorOrOpen; - switch (results) { - | Ok(dists) => - | Error(r) => r |> R.ste - }; - | Error(r) => r |> R.ste - }; - | _ => - "Nothing to show. Try to change the distribution description." - |> R.ste - }} -
-
; - }; -}; - -[@react.component] -let make = () => { - let (reloader, setReloader) = React.useState(() => 1); - let reform = - Form.use( - ~validationStrategy=OnDemand, - ~schema=Form.Validation.Schema([||]), - ~onSubmit=({state}) => {None}, - ~initialState={ - //squiggleString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))", - squiggleString: "mm(normal(5,2), normal(10,2))", - sampleCount: "1000", - outputXYPoints: "1000", - downsampleTo: "", - kernelWidth: "", - diagramStart: "0", - diagramStop: "10", - diagramCount: "20", - }, - (), - ); - - let onSubmit = e => { - e->ReactEvent.Synthetic.preventDefault; - reform.submit(); - }; - - let squiggleString = reform.state.values.squiggleString; - let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString; - let outputXYPoints = - reform.state.values.outputXYPoints |> Js.Float.fromString; - let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString; - let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString; - let diagramStart = reform.state.values.diagramStart |> Js.Float.fromString; - let diagramStop = reform.state.values.diagramStop |> Js.Float.fromString; - let diagramCount = reform.state.values.diagramCount |> Js.Float.fromString; - - let options = - switch (sampleCount, outputXYPoints, downsampleTo) { - | (_, _, _) - when - !Js.Float.isNaN(sampleCount) - && !Js.Float.isNaN(outputXYPoints) - && !Js.Float.isNaN(downsampleTo) - && sampleCount > 10. - && outputXYPoints > 10. => - Some({ - sampleCount: sampleCount |> int_of_float, - outputXYPoints: outputXYPoints |> int_of_float, - downsampleTo: - int_of_float(downsampleTo) > 0 - ? Some(int_of_float(downsampleTo)) : None, - kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth), - diagramStart: diagramStart, - diagramStop: diagramStop, - diagramCount: diagramCount |> int_of_float, - }) - | _ => None - }; - - let demoDist = - React.useMemo1( - () => , - [| - reform.state.values.squiggleString, - reform.state.values.sampleCount, - reform.state.values.outputXYPoints, - reform.state.values.downsampleTo, - reform.state.values.kernelWidth, - reform.state.values.diagramStart, - reform.state.values.diagramStop, - reform.state.values.diagramCount, - reloader |> string_of_int, - |], - ); - - let onReload = _ => { - setReloader(_ => reloader + 1); - }; - -
-
- R.ste} - extra={ - - }> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
demoDist
-
; -}; diff --git a/src/components/DistBuilder.res b/src/components/DistBuilder.res new file mode 100644 index 00000000..2a4ec542 --- /dev/null +++ b/src/components/DistBuilder.res @@ -0,0 +1,271 @@ +open ReForm +open Antd.Grid + +module FormConfig = %lenses( + type state = { + squiggleString: string, + sampleCount: string, + outputXYPoints: string, + downsampleTo: string, + kernelWidth: string, + diagramStart: string, + diagramStop: string, + diagramCount: string, + } +) + +type options = { + sampleCount: int, + outputXYPoints: int, + downsampleTo: option, + kernelWidth: option, + diagramStart: float, + diagramStop: float, + diagramCount: int, +} + +module Form = ReForm.Make(FormConfig) + +module FieldText = { + @react.component + let make = (~field, ~label) => <> + + handleChange(r)} />} + /> + +} +module FieldString = { + @react.component + let make = (~field, ~label) => + + R.ste}> + validate()} + /> + } + /> +} + +module FieldFloat = { + @react.component + let make = (~field, ~label, ~className=Css.style(list{})) => + + R.ste}> + validate()} + className + /> + } + /> +} + +module Styles = { + open Css + let rows = style(list{ + selector(">.ant-col:first-child", list{paddingLeft(em(0.25)), paddingRight(em(0.125))}), + selector(">.ant-col:last-child", list{paddingLeft(em(0.125)), paddingRight(em(0.25))}), + selector( + ">.ant-col:not(:first-child):not(:last-child)", + list{paddingLeft(em(0.125)), paddingRight(em(0.125))}, + ), + }) + let parent = style(list{ + selector(".ant-input-number", list{width(#percent(100.))}), + selector(".anticon", list{verticalAlign(#zero)}), + }) + let form = style(list{backgroundColor(hex("eee")), padding(em(1.))}) + let dist = style(list{padding(em(1.))}) + let spacer = style(list{marginTop(em(1.))}) + let groupA = style(list{ + selector(".ant-input-number-input", list{backgroundColor(hex("fff7db"))}), + }) + let groupB = style(list{ + selector(".ant-input-number-input", list{backgroundColor(hex("eaf4ff"))}), + }) +} + +module DemoDist = { + @react.component + let make = (~squiggleString: string, ~options) => + R.ste}> +
+ {switch options { + | Some(options) => + let inputs1 = ProgramEvaluator.Inputs.make( + ~samplingInputs={ + sampleCount: Some(options.sampleCount), + outputXYPoints: Some(options.outputXYPoints), + kernelWidth: options.kernelWidth, + shapeLength: Some(options.downsampleTo |> E.O.default(1000)), + }, + ~squiggleString, + ~environment=[ + ("K", #SymbolicDist(#Float(1000.0))), + ("M", #SymbolicDist(#Float(1000000.0))), + ("B", #SymbolicDist(#Float(1000000000.0))), + ("T", #SymbolicDist(#Float(1000000000000.0))), + ]->Belt.Map.String.fromArray, + (), + ) + + let distributionList = ProgramEvaluator.evaluateProgram(inputs1) + + let renderExpression = response1 => + switch response1 { + | #DistPlus(distPlus1) => + | #Float(f) => + | #Function((f, a), env) => + // Problem: When it gets the function, it doesn't save state about previous commands + let foo: ProgramEvaluator.Inputs.inputs = { + squiggleString: squiggleString, + samplingInputs: inputs1.samplingInputs, + environment: env, + } + let results = + E.A.Floats.range(options.diagramStart, options.diagramStop, options.diagramCount) + |> E.A.fmap(r => + ProgramEvaluator.evaluateFunction( + foo, + (f, a), + [#SymbolicDist(#Float(r))], + ) |> E.R.bind(_, a => + switch a { + | #DistPlus(d) => Ok((r, DistPlus.T.normalize(d))) + | n => + Js.log2("Error here", n) + Error("wrong type") + } + ) + ) + |> E.A.R.firstErrorOrOpen + switch results { + | Ok(dists) => + | Error(r) => r |> R.ste + } + } + + // Render the list of distributions given by the + switch distributionList { + | Ok(xs) => + let childrenElements = List.map(renderExpression, xs) + Js.Console.log(childrenElements) + @JSX div(~children=childrenElements, ()) + | Error(r) => r |> R.ste + } + | _ => "Nothing to show. Try to change the distribution description." |> R.ste + }} +
+
+} + +@react.component +let make = () => { + let (reloader, setReloader) = React.useState(() => 1) + let reform = Form.use( + ~validationStrategy=OnDemand, + ~schema=Form.Validation.Schema([]), + ~onSubmit=({state}) => None, + ~initialState={ + //squiggleString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))", + squiggleString: "mm(normal(5,2), normal(10,2))", + sampleCount: "1000", + outputXYPoints: "1000", + downsampleTo: "", + kernelWidth: "", + diagramStart: "0", + diagramStop: "10", + diagramCount: "20", + }, + (), + ) + + let onSubmit = e => { + e->ReactEvent.Synthetic.preventDefault + reform.submit() + } + + let squiggleString = reform.state.values.squiggleString + let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString + let outputXYPoints = reform.state.values.outputXYPoints |> Js.Float.fromString + let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString + let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString + let diagramStart = reform.state.values.diagramStart |> Js.Float.fromString + let diagramStop = reform.state.values.diagramStop |> Js.Float.fromString + let diagramCount = reform.state.values.diagramCount |> Js.Float.fromString + + let options = switch (sampleCount, outputXYPoints, downsampleTo) { + | (_, _, _) + if !Js.Float.isNaN(sampleCount) && + (!Js.Float.isNaN(outputXYPoints) && + (!Js.Float.isNaN(downsampleTo) && (sampleCount > 10. && outputXYPoints > 10.))) => + Some({ + sampleCount: sampleCount |> int_of_float, + outputXYPoints: outputXYPoints |> int_of_float, + downsampleTo: int_of_float(downsampleTo) > 0 ? Some(int_of_float(downsampleTo)) : None, + kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth), + diagramStart: diagramStart, + diagramStop: diagramStop, + diagramCount: diagramCount |> int_of_float, + }) + | _ => None + } + + let demoDist = React.useMemo1( + () => , + [ + reform.state.values.squiggleString, + reform.state.values.sampleCount, + reform.state.values.outputXYPoints, + reform.state.values.downsampleTo, + reform.state.values.kernelWidth, + reform.state.values.diagramStart, + reform.state.values.diagramStop, + reform.state.values.diagramCount, + reloader |> string_of_int, + ], + ) + + let onReload = _ => setReloader(_ => reloader + 1) + +
+
+ R.ste} + extra={}> + + + + + + + + + + + + + + + + + + + + + + + + + +
+
demoDist
+
+} diff --git a/src/distPlus/ProgramEvaluator.re b/src/distPlus/ProgramEvaluator.re deleted file mode 100644 index 2776743e..00000000 --- a/src/distPlus/ProgramEvaluator.re +++ /dev/null @@ -1,180 +0,0 @@ -// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here. -module Inputs = { - module SamplingInputs = { - type t = { - sampleCount: option(int), - outputXYPoints: option(int), - kernelWidth: option(float), - shapeLength: option(int), - }; - }; - let defaultRecommendedLength = 100; - let defaultShouldDownsample = true; - - type inputs = { - squiggleString: string, - samplingInputs: SamplingInputs.t, - environment: ExpressionTypes.ExpressionTree.environment, - }; - - let empty: SamplingInputs.t = { - sampleCount: None, - outputXYPoints: None, - kernelWidth: None, - shapeLength: None, - }; - - let make = - ( - ~samplingInputs=empty, - ~squiggleString, - ~environment=ExpressionTypes.ExpressionTree.Environment.empty, - (), - ) - : inputs => { - samplingInputs, - squiggleString, - environment, - }; -}; - -type export = [ - | `DistPlus(ProbExample.DistPlus.t) - | `Float(float) - | `Function( - (array(string), ProbExample.ExpressionTypes.ExpressionTree.node), - ProbExample.ExpressionTypes.ExpressionTree.environment, - ) -]; - -module Internals = { - let addVariable = - ( - {samplingInputs, squiggleString, environment}: Inputs.inputs, - str, - node, - ) - : Inputs.inputs => { - samplingInputs, - squiggleString, - environment: - ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ => - Some(node) - ), - }; - - type outputs = { - graph: ExpressionTypes.ExpressionTree.node, - shape: DistTypes.shape, - }; - let makeOutputs = (graph, shape): outputs => {graph, shape}; - - let makeInputs = - (inputs: Inputs.inputs): ExpressionTypes.ExpressionTree.samplingInputs => { - sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000), - outputXYPoints: - inputs.samplingInputs.outputXYPoints |> E.O.default(10000), - kernelWidth: inputs.samplingInputs.kernelWidth, - shapeLength: inputs.samplingInputs.shapeLength |> E.O.default(10000), - }; - - let runNode = (inputs, node) => { - ExpressionTree.toLeaf(makeInputs(inputs), inputs.environment, node); - }; - - let runProgram = (inputs: Inputs.inputs, p: ExpressionTypes.Program.program) => { - let ins = ref(inputs); - p - |> E.A.fmap( - fun - | `Assignment(name, node) => { - ins := addVariable(ins^, name, node); - None; - } - | `Expression(node) => - Some( - runNode(ins^, node) |> E.R.fmap(r => (ins^.environment, r)), - ), - ) - |> E.A.O.concatSomes - |> E.A.R.firstErrorOrOpen; - }; - - let inputsToLeaf = (inputs: Inputs.inputs) => { - MathJsParser.fromString(inputs.squiggleString) - |> E.R.bind(_, g => runProgram(inputs, g)) - |> E.R.bind(_, r => E.A.last(r) |> E.O.toResult("No rendered lines")); - }; - - let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) => { - DistPlus.make(~shape, ~squiggleString=Some(inputs.squiggleString), ()); - }; -}; - -let renderIfNeeded = - (inputs: Inputs.inputs, node: ExpressionTypes.ExpressionTree.node) - : result(ExpressionTypes.ExpressionTree.node, string) => - node - |> ( - fun - | `Normalize(_) as n - | `SymbolicDist(_) as n => { - `Render(n) - |> Internals.runNode(inputs) - |> ( - fun - | Ok(`RenderedDist(_)) as r => r - | Error(r) => Error(r) - | _ => Error("Didn't render, but intended to") - ); - } - | n => Ok(n) - ); - -// TODO: Consider using ExpressionTypes.ExpressionTree.getFloat or similar in this function -let coersionToExportedTypes = - ( - inputs, - env: ProbExample.ExpressionTypes.ExpressionTree.environment, - node: ExpressionTypes.ExpressionTree.node, - ) - : result(export, string) => - node - |> renderIfNeeded(inputs) - |> E.R.bind( - _, - fun - | `RenderedDist(Discrete({xyShape: {xs: [|x|], ys: [|1.0|]}})) => - Ok(`Float(x)) - | `SymbolicDist(`Float(x)) => Ok(`Float(x)) - | `RenderedDist(n) => - Ok(`DistPlus(Internals.outputToDistPlus(inputs, n))) - | `Function(n) => Ok(`Function((n, env))) - | n => - Error( - "Didn't output a rendered distribution. Format:" - ++ ExpressionTree.toString(n), - ), - ); - -let evaluateProgram = (inputs: Inputs.inputs) => { - inputs - |> Internals.inputsToLeaf - |> E.R.bind(_, ((a, b)) => coersionToExportedTypes(inputs, a, b)); -}; - -let evaluateFunction = - ( - inputs: Inputs.inputs, - fn: (array(string), ExpressionTypes.ExpressionTree.node), - fnInputs, - ) => { - let output = - ExpressionTree.runFunction( - Internals.makeInputs(inputs), - inputs.environment, - fnInputs, - fn, - ); - output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment)); -}; diff --git a/src/distPlus/ProgramEvaluator.res b/src/distPlus/ProgramEvaluator.res new file mode 100644 index 00000000..7ab92f4c --- /dev/null +++ b/src/distPlus/ProgramEvaluator.res @@ -0,0 +1,171 @@ +// TODO: This setup is more confusing than it should be, there's more work to do in cleanup here. +module Inputs = { + module SamplingInputs = { + type t = { + sampleCount: option, + outputXYPoints: option, + kernelWidth: option, + shapeLength: option, + } + } + let defaultRecommendedLength = 100 + let defaultShouldDownsample = true + + type inputs = { + squiggleString: string, + samplingInputs: SamplingInputs.t, + environment: ExpressionTypes.ExpressionTree.environment, + } + + let empty: SamplingInputs.t = { + sampleCount: None, + outputXYPoints: None, + kernelWidth: None, + shapeLength: None, + } + + let make = ( + ~samplingInputs=empty, + ~squiggleString, + ~environment=ExpressionTypes.ExpressionTree.Environment.empty, + (), + ): inputs => { + samplingInputs: samplingInputs, + squiggleString: squiggleString, + environment: environment, + } +} + +type \"export" = [ + | #DistPlus(ProbExample.DistPlus.t) + | #Float(float) + | #Function( + (array, ProbExample.ExpressionTypes.ExpressionTree.node), + ProbExample.ExpressionTypes.ExpressionTree.environment, + ) +] + +module Internals = { + let addVariable = ( + {samplingInputs, squiggleString, environment}: Inputs.inputs, + str, + node, + ): Inputs.inputs => { + samplingInputs: samplingInputs, + squiggleString: squiggleString, + environment: ExpressionTypes.ExpressionTree.Environment.update(environment, str, _ => Some( + node, + )), + } + + type outputs = { + graph: ExpressionTypes.ExpressionTree.node, + shape: DistTypes.shape, + } + let makeOutputs = (graph, shape): outputs => {graph: graph, shape: shape} + + let makeInputs = (inputs: Inputs.inputs): ExpressionTypes.ExpressionTree.samplingInputs => { + sampleCount: inputs.samplingInputs.sampleCount |> E.O.default(10000), + outputXYPoints: inputs.samplingInputs.outputXYPoints |> E.O.default(10000), + kernelWidth: inputs.samplingInputs.kernelWidth, + shapeLength: inputs.samplingInputs.shapeLength |> E.O.default(10000), + } + + let runNode = (inputs, node) => + ExpressionTree.toLeaf(makeInputs(inputs), inputs.environment, node) + + let runProgram = (inputs: Inputs.inputs, p: ExpressionTypes.Program.program) => { + let ins = ref(inputs) + p + |> E.A.fmap(x => + switch x { + | #Assignment(name, node) => + ins := addVariable(ins.contents, name, node) + None + | #Expression(node) => + Some(runNode(ins.contents, node) |> E.R.fmap(r => (ins.contents.environment, r))) + } + ) + |> E.A.O.concatSomes + |> E.A.R.firstErrorOrOpen + } + + let inputsToLeaf = (inputs: Inputs.inputs) => + MathJsParser.fromString(inputs.squiggleString) + |> E.R.bind(_, g => runProgram(inputs, g)) + + let outputToDistPlus = (inputs: Inputs.inputs, shape: DistTypes.shape) => + DistPlus.make(~shape, ~squiggleString=Some(inputs.squiggleString), ()) +} + +let renderIfNeeded = (inputs: Inputs.inputs, node: ExpressionTypes.ExpressionTree.node): result< + ExpressionTypes.ExpressionTree.node, + string, +> => + node |> ( + x => + switch x { + | #Normalize(_) as n + | #SymbolicDist(_) as n => + #Render(n) + |> Internals.runNode(inputs) + |> ( + x => + switch x { + | Ok(#RenderedDist(_)) as r => r + | Error(r) => Error(r) + | _ => Error("Didn't render, but intended to") + } + ) + | n => Ok(n) + } + ) + +// TODO: Consider using ExpressionTypes.ExpressionTree.getFloat or similar in this function +let coersionToExportedTypes = ( + inputs, + env: ProbExample.ExpressionTypes.ExpressionTree.environment, + node: ExpressionTypes.ExpressionTree.node, +): result<\"export", string> => + node + |> renderIfNeeded(inputs) + |> E.R.bind(_, x => + switch x { + | #RenderedDist(Discrete({xyShape: {xs: [x], ys: [1.0]}})) => Ok(#Float(x)) + | #SymbolicDist(#Float(x)) => Ok(#Float(x)) + | #RenderedDist(n) => Ok(#DistPlus(Internals.outputToDistPlus(inputs, n))) + | #Function(n) => Ok(#Function(n, env)) + | n => Error("Didn't output a rendered distribution. Format:" ++ ExpressionTree.toString(n)) + } + ) + +let rec mapM = (f, xs) => + switch xs { + | list{} => Ok(list{}) + | list{x, ...rest} => + switch f(x) { + | Error(err) => Error(err) + | Ok(val) => + switch mapM(f, rest) { + | Error(err) => Error(err) + | Ok(restList) => Ok(list{val, ...restList}) + } + } + } + +let evaluateProgram = (inputs: Inputs.inputs) => + inputs |> Internals.inputsToLeaf |> E.R.bind(_, xs => mapM(((a, b)) => coersionToExportedTypes(inputs, a, b), (Array.to_list(xs)))) + +let evaluateFunction = ( + inputs: Inputs.inputs, + fn: (array, ExpressionTypes.ExpressionTree.node), + fnInputs, +) => { + let output = ExpressionTree.runFunction( + Internals.makeInputs(inputs), + inputs.environment, + fnInputs, + fn, + ) + output |> E.R.bind(_, coersionToExportedTypes(inputs, inputs.environment)) +} diff --git a/yarn.lock b/yarn.lock index eafdb353..c370c9ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1382,6 +1382,11 @@ "@parcel/utils" "^1.11.0" physical-cpu-count "^2.0.0" +"@rescript/react@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@rescript/react/-/react-0.10.3.tgz#a2a8bed6b017940ec26c2154764b350f50348889" + integrity sha512-Lf9rzrR3bQPKJjOK3PBRa/B3xrJ7CqQ1HYr9VHPVxJidarIJJFZBhj0Dg1uZURX+Wg/xiP0PHFxXmdj2bK8Vxw== + "@sinonjs/commons@^1.7.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2"