diff --git a/packages/components/src/components/DistributionChart.tsx b/packages/components/src/components/DistributionChart.tsx
index 6d4e7c55..7556da26 100644
--- a/packages/components/src/components/DistributionChart.tsx
+++ b/packages/components/src/components/DistributionChart.tsx
@@ -17,6 +17,8 @@ import {
DistributionChartSpecOptions,
} from "../lib/distributionSpecBuilder";
import { NumberShower } from "./NumberShower";
+import { Plot, parsePlot } from "../lib/plotting";
+import { flattenResult, all } from "../lib/utility";
export type DistributionPlottingSettings = {
/** Whether to show a summary of means, stdev, percentiles etc */
@@ -25,12 +27,6 @@ export type DistributionPlottingSettings = {
showControls: boolean;
} & DistributionChartSpecOptions;
-export type LabeledDistribution = { name: string; distribution: Distribution };
-
-export type Plot = {
- distributions: LabeledDistribution[];
-};
-
export type DistributionChartProps = {
plot: Plot;
width?: number;
@@ -42,99 +38,6 @@ export function defaultPlot(distribution: Distribution): Plot {
return { distributions: [{ name: "default", distribution }] };
}
-function error(err: b): result {
- return { tag: "Error", value: err };
-}
-
-function ok(x: a): result {
- return { tag: "Ok", value: x };
-}
-
-function parseString(expr: squiggleExpression): result {
- if (expr.tag === "string") {
- return ok(expr.value);
- } else {
- return error("Expression was not string");
- }
-}
-
-function parseRecord(
- expr: squiggleExpression
-): result<{ [key: string]: squiggleExpression }, string> {
- if (expr.tag === "record") {
- return ok(expr.value);
- } else {
- return error("Expression was not a record");
- }
-}
-
-function parseDistribution(
- expr: squiggleExpression
-): result {
- if (expr.tag === "distribution") {
- return ok(expr.value);
- } else {
- return error("Expression was not a distribution");
- }
-}
-
-function parseArray(
- expr: squiggleExpression
-): result {
- if (expr.tag === "array") {
- return ok(expr.value);
- } else {
- return error("Expression was not a distribution");
- }
-}
-
-function parseField(
- record: { [key: string]: squiggleExpression },
- field: string,
- parser: (expr: squiggleExpression) => result
-): result {
- if (record[field]) {
- return parser(record[field]);
- } else {
- return error("record does not have field " + field);
- }
-}
-
-function resultBind(
- x: result,
- fn: (y: a) => result
-): result {
- if (x.tag === "Ok") {
- return fn(x.value);
- } else {
- return x;
- }
-}
-
-function parseLabeledDistribution(
- x: squiggleExpression
-): result {
- return resultBind(parseRecord(x), (record) =>
- resultBind(parseField(record, "name", parseString), (name) =>
- resultBind(
- parseField(record, "distribution", parseDistribution),
- (distribution) => ok({ name, distribution })
- )
- )
- );
-}
-
-function parsePlot(record: {
- [key: string]: squiggleExpression;
-}): result {
- return resultBind(parseField(record, "distributions", parseArray), (array) =>
- resultBind(
- flattenResult(array.map(parseLabeledDistribution)),
- (distributions) => ok({ distributions })
- )
- );
-}
-
export function makePlot(record: {
[key: string]: squiggleExpression;
}): Plot | void {
@@ -143,26 +46,6 @@ export function makePlot(record: {
return plotResult.value;
}
}
-function all(arr: boolean[]): boolean {
- return arr.reduce((x, y) => x && y, true);
-}
-
-function flattenResult(x: result[]): result {
- if (x.length === 0) {
- return { tag: "Ok", value: [] };
- } else {
- if (x[0].tag === "Error") {
- return x[0];
- } else {
- let rest = flattenResult(x.splice(1));
- if (rest.tag === "Error") {
- return rest;
- } else {
- return { tag: "Ok", value: [x[0].value].concat(rest.value) };
- }
- }
- }
-}
export const DistributionChart: React.FC = (props) => {
const {
diff --git a/packages/components/src/lib/plotting.ts b/packages/components/src/lib/plotting.ts
new file mode 100644
index 00000000..5b7ca31d
--- /dev/null
+++ b/packages/components/src/lib/plotting.ts
@@ -0,0 +1,90 @@
+import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang";
+import { flattenResult, resultBind } from "./utility";
+
+export type LabeledDistribution = { name: string; distribution: Distribution };
+
+export type Plot = {
+ distributions: LabeledDistribution[];
+};
+
+function error(err: b): result {
+ return { tag: "Error", value: err };
+}
+
+function ok(x: a): result {
+ return { tag: "Ok", value: x };
+}
+
+function parseString(expr: squiggleExpression): result {
+ if (expr.tag === "string") {
+ return ok(expr.value);
+ } else {
+ return error("Expression was not string");
+ }
+}
+
+function parseRecord(
+ expr: squiggleExpression
+): result<{ [key: string]: squiggleExpression }, string> {
+ if (expr.tag === "record") {
+ return ok(expr.value);
+ } else {
+ return error("Expression was not a record");
+ }
+}
+
+function parseDistribution(
+ expr: squiggleExpression
+): result {
+ if (expr.tag === "distribution") {
+ return ok(expr.value);
+ } else {
+ return error("Expression was not a distribution");
+ }
+}
+
+function parseArray(
+ expr: squiggleExpression
+): result {
+ if (expr.tag === "array") {
+ return ok(expr.value);
+ } else {
+ return error("Expression was not a distribution");
+ }
+}
+
+function parseField(
+ record: { [key: string]: squiggleExpression },
+ field: string,
+ parser: (expr: squiggleExpression) => result
+): result {
+ if (record[field]) {
+ return parser(record[field]);
+ } else {
+ return error("record does not have field " + field);
+ }
+}
+
+function parseLabeledDistribution(
+ x: squiggleExpression
+): result {
+ return resultBind(parseRecord(x), (record) =>
+ resultBind(parseField(record, "name", parseString), (name) =>
+ resultBind(
+ parseField(record, "distribution", parseDistribution),
+ (distribution) => ok({ name, distribution })
+ )
+ )
+ );
+}
+
+export function parsePlot(record: {
+ [key: string]: squiggleExpression;
+}): result {
+ return resultBind(parseField(record, "distributions", parseArray), (array) =>
+ resultBind(
+ flattenResult(array.map(parseLabeledDistribution)),
+ (distributions) => ok({ distributions })
+ )
+ );
+}
diff --git a/packages/components/src/lib/utility.ts b/packages/components/src/lib/utility.ts
new file mode 100644
index 00000000..4a5ecc6b
--- /dev/null
+++ b/packages/components/src/lib/utility.ts
@@ -0,0 +1,33 @@
+import { result } from "@quri/squiggle-lang";
+
+export function flattenResult(x: result[]): result {
+ if (x.length === 0) {
+ return { tag: "Ok", value: [] };
+ } else {
+ if (x[0].tag === "Error") {
+ return x[0];
+ } else {
+ let rest = flattenResult(x.splice(1));
+ if (rest.tag === "Error") {
+ return rest;
+ } else {
+ return { tag: "Ok", value: [x[0].value].concat(rest.value) };
+ }
+ }
+ }
+}
+
+export function resultBind(
+ x: result,
+ fn: (y: a) => result
+): result {
+ if (x.tag === "Ok") {
+ return fn(x.value);
+ } else {
+ return x;
+ }
+}
+
+export function all(arr: boolean[]): boolean {
+ return arr.reduce((x, y) => x && y, true);
+}