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"