Merge branch 'epic-reducer-project' of github.com:quantified-uncertainty/squiggle into epic-reducer-project

This commit is contained in:
Umur Ozkul 2022-09-01 14:52:14 +02:00
commit 1fe0aefd6b
59 changed files with 1347 additions and 1509 deletions

View File

@ -1,11 +1,11 @@
import * as React from "react"; import * as React from "react";
import { import {
Distribution, SqDistribution,
result, result,
distributionError, SqDistributionError,
distributionErrorToString,
squiggleExpression,
resultMap, resultMap,
SqRecord,
environment,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { Vega } from "react-vega"; import { Vega } from "react-vega";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
@ -28,17 +28,16 @@ export type DistributionPlottingSettings = {
export type DistributionChartProps = { export type DistributionChartProps = {
plot: Plot; plot: Plot;
environment: environment;
width?: number; width?: number;
height: number; height: number;
} & DistributionPlottingSettings; } & DistributionPlottingSettings;
export function defaultPlot(distribution: Distribution): Plot { export function defaultPlot(distribution: SqDistribution): Plot {
return { distributions: [{ name: "default", distribution }] }; return { distributions: [{ name: "default", distribution }] };
} }
export function makePlot(record: { export function makePlot(record: SqRecord): Plot | void {
[key: string]: squiggleExpression;
}): Plot | void {
const plotResult = parsePlot(record); const plotResult = parsePlot(record);
if (plotResult.tag === "Ok") { if (plotResult.tag === "Ok") {
return plotResult.value; return plotResult.value;
@ -46,22 +45,29 @@ export function makePlot(record: {
} }
export const DistributionChart: React.FC<DistributionChartProps> = (props) => { export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
const { plot, height, showSummary, width, logX, actions = false } = props; const {
plot,
environment,
height,
showSummary,
width,
logX,
actions = false,
} = props;
const [sized] = useSize((size) => { const [sized] = useSize((size) => {
let shapes = flattenResult( const shapes = flattenResult(
plot.distributions.map((x) => plot.distributions.map((x) => {
resultMap(x.distribution.pointSet(), (shape) => ({ return resultMap(x.distribution.pointSet(environment), (pointSet) => ({
...pointSet.asShape(),
name: x.name, name: x.name,
// color: x.color, // not supported yet // color: x.color, // not supported yet
continuous: shape.continuous, }));
discrete: shape.discrete, })
}))
)
); );
if (shapes.tag === "Error") { if (shapes.tag === "Error") {
return ( return (
<ErrorAlert heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(shapes.value)} {shapes.value.toString()}
</ErrorAlert> </ErrorAlert>
); );
} }
@ -96,7 +102,10 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
)} )}
<div className="flex justify-center"> <div className="flex justify-center">
{showSummary && plot.distributions.length === 1 && ( {showSummary && plot.distributions.length === 1 && (
<SummaryTable distribution={plot.distributions[0].distribution} /> <SummaryTable
distribution={plot.distributions[0].distribution}
environment={environment}
/>
)} )}
</div> </div>
</div> </div>
@ -120,32 +129,36 @@ const Cell: React.FC<{ children: React.ReactNode }> = ({ children }) => (
); );
type SummaryTableProps = { type SummaryTableProps = {
distribution: Distribution; distribution: SqDistribution;
environment: environment;
}; };
const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => { const SummaryTable: React.FC<SummaryTableProps> = ({
const mean = distribution.mean(); distribution,
const stdev = distribution.stdev(); environment,
const p5 = distribution.inv(0.05); }) => {
const p10 = distribution.inv(0.1); const mean = distribution.mean(environment);
const p25 = distribution.inv(0.25); const stdev = distribution.stdev(environment);
const p50 = distribution.inv(0.5); const p5 = distribution.inv(environment, 0.05);
const p75 = distribution.inv(0.75); const p10 = distribution.inv(environment, 0.1);
const p90 = distribution.inv(0.9); const p25 = distribution.inv(environment, 0.25);
const p95 = distribution.inv(0.95); const p50 = distribution.inv(environment, 0.5);
const p75 = distribution.inv(environment, 0.75);
const p90 = distribution.inv(environment, 0.9);
const p95 = distribution.inv(environment, 0.95);
const hasResult = (x: result<number, distributionError>): boolean => const hasResult = (x: result<number, SqDistributionError>): boolean =>
x.tag === "Ok"; x.tag === "Ok";
const unwrapResult = ( const unwrapResult = (
x: result<number, distributionError> x: result<number, SqDistributionError>
): React.ReactNode => { ): React.ReactNode => {
if (x.tag === "Ok") { if (x.tag === "Ok") {
return <NumberShower number={x.value} />; return <NumberShower number={x.value} />;
} else { } else {
return ( return (
<ErrorAlert heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(x.value)} {x.value.toString()}
</ErrorAlert> </ErrorAlert>
); );
} }

View File

@ -1,10 +1,5 @@
import * as React from "react"; import * as React from "react";
import { import { SqLambda, environment, SqValueTag } from "@quri/squiggle-lang";
lambdaValue,
environment,
runForeign,
errorValueToString,
} from "@quri/squiggle-lang";
import { FunctionChart1Dist } from "./FunctionChart1Dist"; import { FunctionChart1Dist } from "./FunctionChart1Dist";
import { FunctionChart1Number } from "./FunctionChart1Number"; import { FunctionChart1Number } from "./FunctionChart1Number";
import { DistributionPlottingSettings } from "./DistributionChart"; import { DistributionPlottingSettings } from "./DistributionChart";
@ -17,7 +12,7 @@ export type FunctionChartSettings = {
}; };
interface FunctionChartProps { interface FunctionChartProps {
fn: lambdaValue; fn: SqLambda;
chartSettings: FunctionChartSettings; chartSettings: FunctionChartSettings;
distributionPlotSettings: DistributionPlottingSettings; distributionPlotSettings: DistributionPlottingSettings;
environment: environment; environment: environment;
@ -38,8 +33,8 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
</MessageAlert> </MessageAlert>
); );
} }
const result1 = runForeign(fn, [chartSettings.start], environment); const result1 = fn.call([chartSettings.start]);
const result2 = runForeign(fn, [chartSettings.stop], environment); const result2 = fn.call([chartSettings.stop]);
const getValidResult = () => { const getValidResult = () => {
if (result1.tag === "Ok") { if (result1.tag === "Ok") {
return result1; return result1;
@ -53,14 +48,12 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
if (validResult.tag === "Error") { if (validResult.tag === "Error") {
return ( return (
<ErrorAlert heading="Error"> <ErrorAlert heading="Error">{validResult.value.toString()}</ErrorAlert>
{errorValueToString(validResult.value)}
</ErrorAlert>
); );
} }
switch (validResult.value.tag) { switch (validResult.value.tag) {
case "distribution": case SqValueTag.Distribution:
return ( return (
<FunctionChart1Dist <FunctionChart1Dist
fn={fn} fn={fn}
@ -70,7 +63,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
distributionPlotSettings={distributionPlotSettings} distributionPlotSettings={distributionPlotSettings}
/> />
); );
case "number": case SqValueTag.Number:
return ( return (
<FunctionChart1Number <FunctionChart1Number
fn={fn} fn={fn}

View File

@ -2,14 +2,13 @@ import * as React from "react";
import _ from "lodash"; import _ from "lodash";
import type { Spec } from "vega"; import type { Spec } from "vega";
import { import {
Distribution, SqDistribution,
result, result,
lambdaValue, SqLambda,
environment, environment,
runForeign, SqError,
squiggleExpression, SqValue,
errorValue, SqValueTag,
errorValueToString,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega"; import { createClassFromSpec } from "react-vega";
import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
@ -46,7 +45,7 @@ export type FunctionChartSettings = {
}; };
interface FunctionChart1DistProps { interface FunctionChart1DistProps {
fn: lambdaValue; fn: SqLambda;
chartSettings: FunctionChartSettings; chartSettings: FunctionChartSettings;
distributionPlotSettings: DistributionPlottingSettings; distributionPlotSettings: DistributionPlottingSettings;
environment: environment; environment: environment;
@ -77,9 +76,17 @@ type errors = _.Dictionary<
}[] }[]
>; >;
type point = { x: number; value: result<Distribution, string> }; type point = { x: number; value: result<SqDistribution, string> };
let getPercentiles = ({ chartSettings, fn, environment }) => { let getPercentiles = ({
chartSettings,
fn,
environment,
}: {
chartSettings: FunctionChartSettings;
fn: SqLambda;
environment: environment;
}) => {
let chartPointsToRender = _rangeByCount( let chartPointsToRender = _rangeByCount(
chartSettings.start, chartSettings.start,
chartSettings.stop, chartSettings.stop,
@ -87,9 +94,9 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
); );
let chartPointsData: point[] = chartPointsToRender.map((x) => { let chartPointsData: point[] = chartPointsToRender.map((x) => {
let result = runForeign(fn, [x], environment); let result = fn.call([x]);
if (result.tag === "Ok") { if (result.tag === "Ok") {
if (result.value.tag === "distribution") { if (result.value.tag === SqValueTag.Distribution) {
return { x, value: { tag: "Ok", value: result.value.value } }; return { x, value: { tag: "Ok", value: result.value.value } };
} else { } else {
return { return {
@ -104,13 +111,13 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
} else { } else {
return { return {
x, x,
value: { tag: "Error", value: errorValueToString(result.value) }, value: { tag: "Error", value: result.value.toString() },
}; };
} }
}); });
let initialPartition: [ let initialPartition: [
{ x: number; value: Distribution }[], { x: number; value: SqDistribution }[],
{ x: number; value: string }[] { x: number; value: string }[]
] = [[], []]; ] = [[], []];
@ -129,22 +136,22 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
// We convert it to to a pointSet distribution first, so that in case its a sample set // We convert it to to a pointSet distribution first, so that in case its a sample set
// distribution, it doesn't internally convert it to a pointSet distribution for every // distribution, it doesn't internally convert it to a pointSet distribution for every
// single inv() call. // single inv() call.
let toPointSet: Distribution = unwrap(value.toPointSet()); let toPointSet = unwrap(value.pointSet(environment)).asDistribution();
return { return {
x: x, x: x,
p1: unwrap(toPointSet.inv(0.01)), p1: unwrap(toPointSet.inv(environment, 0.01)),
p5: unwrap(toPointSet.inv(0.05)), p5: unwrap(toPointSet.inv(environment, 0.05)),
p10: unwrap(toPointSet.inv(0.1)), p10: unwrap(toPointSet.inv(environment, 0.1)),
p20: unwrap(toPointSet.inv(0.2)), p20: unwrap(toPointSet.inv(environment, 0.2)),
p30: unwrap(toPointSet.inv(0.3)), p30: unwrap(toPointSet.inv(environment, 0.3)),
p40: unwrap(toPointSet.inv(0.4)), p40: unwrap(toPointSet.inv(environment, 0.4)),
p50: unwrap(toPointSet.inv(0.5)), p50: unwrap(toPointSet.inv(environment, 0.5)),
p60: unwrap(toPointSet.inv(0.6)), p60: unwrap(toPointSet.inv(environment, 0.6)),
p70: unwrap(toPointSet.inv(0.7)), p70: unwrap(toPointSet.inv(environment, 0.7)),
p80: unwrap(toPointSet.inv(0.8)), p80: unwrap(toPointSet.inv(environment, 0.8)),
p90: unwrap(toPointSet.inv(0.9)), p90: unwrap(toPointSet.inv(environment, 0.9)),
p95: unwrap(toPointSet.inv(0.95)), p95: unwrap(toPointSet.inv(environment, 0.95)),
p99: unwrap(toPointSet.inv(0.99)), p99: unwrap(toPointSet.inv(environment, 0.99)),
}; };
}); });
@ -168,19 +175,20 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
const signalListeners = { mousemove: handleHover, mouseout: handleOut }; const signalListeners = { mousemove: handleHover, mouseout: handleOut };
//TODO: This custom error handling is a bit hacky and should be improved. //TODO: This custom error handling is a bit hacky and should be improved.
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay let mouseItem: result<SqValue, SqError> = !!mouseOverlay
? runForeign(fn, [mouseOverlay], environment) ? fn.call([mouseOverlay])
: { : {
tag: "Error", tag: "Error",
value: { value: SqError.createOtherError(
tag: "RETodo", "Hover x-coordinate returned NaN. Expected a number."
value: "Hover x-coordinate returned NaN. Expected a number.", ),
},
}; };
let showChart = let showChart =
mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? ( mouseItem.tag === "Ok" &&
mouseItem.value.tag === SqValueTag.Distribution ? (
<DistributionChart <DistributionChart
plot={defaultPlot(mouseItem.value.value)} plot={defaultPlot(mouseItem.value.value)}
environment={environment}
width={400} width={400}
height={50} height={50}
{...distributionPlotSettings} {...distributionPlotSettings}

View File

@ -1,16 +1,11 @@
import * as React from "react"; import * as React from "react";
import _ from "lodash"; import _ from "lodash";
import type { Spec } from "vega"; import type { Spec } from "vega";
import { import { result, SqLambda, environment, SqValueTag } from "@quri/squiggle-lang";
result,
lambdaValue,
environment,
runForeign,
errorValueToString,
} from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega"; import { createClassFromSpec } from "react-vega";
import * as lineChartSpec from "../vega-specs/spec-line-chart.json"; import * as lineChartSpec from "../vega-specs/spec-line-chart.json";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
import { squiggleValueTag } from "@quri/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag";
let SquiggleLineChart = createClassFromSpec({ let SquiggleLineChart = createClassFromSpec({
spec: lineChartSpec as Spec, spec: lineChartSpec as Spec,
@ -30,7 +25,7 @@ export type FunctionChartSettings = {
}; };
interface FunctionChart1NumberProps { interface FunctionChart1NumberProps {
fn: lambdaValue; fn: SqLambda;
chartSettings: FunctionChartSettings; chartSettings: FunctionChartSettings;
environment: environment; environment: environment;
height: number; height: number;
@ -38,7 +33,15 @@ interface FunctionChart1NumberProps {
type point = { x: number; value: result<number, string> }; type point = { x: number; value: result<number, string> };
let getFunctionImage = ({ chartSettings, fn, environment }) => { let getFunctionImage = ({
chartSettings,
fn,
environment,
}: {
chartSettings: FunctionChartSettings;
fn: SqLambda;
environment: environment;
}) => {
let chartPointsToRender = _rangeByCount( let chartPointsToRender = _rangeByCount(
chartSettings.start, chartSettings.start,
chartSettings.stop, chartSettings.stop,
@ -46,9 +49,9 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => {
); );
let chartPointsData: point[] = chartPointsToRender.map((x) => { let chartPointsData: point[] = chartPointsToRender.map((x) => {
let result = runForeign(fn, [x], environment); let result = fn.call([x]);
if (result.tag === "Ok") { if (result.tag === "Ok") {
if (result.value.tag == "number") { if (result.value.tag === SqValueTag.Number) {
return { x, value: { tag: "Ok", value: result.value.value } }; return { x, value: { tag: "Ok", value: result.value.value } };
} else { } else {
return { return {
@ -62,7 +65,7 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => {
} else { } else {
return { return {
x, x,
value: { tag: "Error", value: errorValueToString(result.value) }, value: { tag: "Error", value: result.value.toString() },
}; };
} }
}); });

View File

@ -1,12 +1,11 @@
import * as React from "react"; import * as React from "react";
import { import {
squiggleExpression, SqValue,
bindings,
environment, environment,
jsImports,
defaultImports,
defaultBindings,
defaultEnvironment, defaultEnvironment,
resultMap,
SqValueLocation,
SqValueTag,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { useSquiggle } from "../lib/hooks"; import { useSquiggle } from "../lib/hooks";
import { SquiggleViewer } from "./SquiggleViewer"; import { SquiggleViewer } from "./SquiggleViewer";
@ -27,14 +26,12 @@ export interface SquiggleChartProps {
/** If the result is a function, the amount of stops sampled */ /** If the result is a function, the amount of stops sampled */
diagramCount?: number; diagramCount?: number;
/** When the squiggle code gets reevaluated */ /** When the squiggle code gets reevaluated */
onChange?(expr: squiggleExpression | undefined): void; onChange?(expr: SqValue | undefined): void;
/** CSS width of the element */ /** CSS width of the element */
width?: number; width?: number;
height?: number; height?: number;
/** Bindings of previous variables declared */
bindings?: bindings;
/** JS imported parameters */ /** JS imported parameters */
jsImports?: jsImports; // jsImports?: jsImports;
/** Whether to show a summary of the distribution */ /** Whether to show a summary of the distribution */
showSummary?: boolean; showSummary?: boolean;
/** Set the x scale to be logarithmic by deault */ /** Set the x scale to be logarithmic by deault */
@ -65,8 +62,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
environment, environment,
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
height = 200, height = 200,
bindings = defaultBindings, // jsImports = defaultImports,
jsImports = defaultImports,
showSummary = false, showSummary = false,
width, width,
logX = false, logX = false,
@ -82,11 +78,10 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
distributionChartActions, distributionChartActions,
enableLocalSettings = false, enableLocalSettings = false,
}) => { }) => {
const result = useSquiggle({ const { result, bindings } = useSquiggle({
code, code,
bindings,
environment, environment,
jsImports, // jsImports,
onChange, onChange,
executionId, executionId,
}); });
@ -109,9 +104,13 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
count: diagramCount, count: diagramCount,
}; };
const resultToRender = resultMap(result, (value) =>
value.tag === SqValueTag.Void ? bindings.asValue() : value
);
return ( return (
<SquiggleViewer <SquiggleViewer
result={result} result={resultToRender}
width={width} width={width}
height={height} height={height}
distributionPlotSettings={distributionPlotSettings} distributionPlotSettings={distributionPlotSettings}

View File

@ -1,11 +1,8 @@
import React from "react"; import React from "react";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
import { environment, bindings, jsImports } from "@quri/squiggle-lang";
import { defaultImports, defaultBindings } from "@quri/squiggle-lang";
import { SquiggleContainer } from "./SquiggleContainer"; import { SquiggleContainer } from "./SquiggleContainer";
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
import { useSquigglePartial, useMaybeControlledValue } from "../lib/hooks"; import { useMaybeControlledValue } from "../lib/hooks";
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
const WrappedCodeEditor: React.FC<{ const WrappedCodeEditor: React.FC<{
code: string; code: string;
@ -42,51 +39,3 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
</SquiggleContainer> </SquiggleContainer>
); );
}; };
export interface SquigglePartialProps {
/** The text inside the input (controlled) */
code?: string;
/** The default text inside the input (unControlled) */
defaultCode?: string;
/** when the environment changes. Used again for notebook magic*/
onChange?(expr: bindings | undefined): void;
/** When the code changes */
onCodeChange?(code: string): void;
/** Previously declared variables */
bindings?: bindings;
/** If the output requires monte carlo sampling, the amount of samples */
environment?: environment;
/** Variables imported from js */
jsImports?: jsImports;
}
export const SquigglePartial: React.FC<SquigglePartialProps> = ({
code: controlledCode,
defaultCode = "",
onChange,
onCodeChange,
bindings = defaultBindings,
environment,
jsImports = defaultImports,
}: SquigglePartialProps) => {
const [code, setCode] = useMaybeControlledValue<string>({
value: controlledCode,
defaultValue: defaultCode,
onChange: onCodeChange,
});
const result = useSquigglePartial({
code,
bindings,
environment,
jsImports,
onChange,
});
return (
<SquiggleContainer>
<WrappedCodeEditor code={code} setCode={setCode} />
{result.tag !== "Ok" ? <SquiggleErrorAlert error={result.value} /> : null}
</SquiggleContainer>
);
};

View File

@ -1,52 +0,0 @@
import React from "react";
import { SquiggleEditor } from "./SquiggleEditor";
import type { SquiggleEditorProps } from "./SquiggleEditor";
import { runPartial, defaultBindings } from "@quri/squiggle-lang";
import type {
result,
errorValue,
bindings as bindingsType,
} from "@quri/squiggle-lang";
function resultDefault(x: result<bindingsType, errorValue>): bindingsType {
switch (x.tag) {
case "Ok":
return x.value;
case "Error":
return defaultBindings;
}
}
export type SquiggleEditorWithImportedBindingsProps = SquiggleEditorProps & {
bindingsImportUrl: string;
};
export const SquiggleEditorWithImportedBindings: React.FC<
SquiggleEditorWithImportedBindingsProps
> = (props) => {
const { bindingsImportUrl, ...editorProps } = props;
const [bindingsResult, setBindingsResult] = React.useState({
tag: "Ok",
value: defaultBindings,
} as result<bindingsType, errorValue>);
React.useEffect(() => {
async function retrieveBindings(fileName: string) {
let contents = await fetch(fileName).then((response) => {
return response.text();
});
setBindingsResult(
runPartial(
contents,
editorProps.bindings,
editorProps.environment,
editorProps.jsImports
)
);
}
retrieveBindings(bindingsImportUrl);
}, [bindingsImportUrl]);
const deliveredBindings = resultDefault(bindingsResult);
return (
<SquiggleEditor {...{ ...editorProps, bindings: deliveredBindings }} />
);
};

View File

@ -1,11 +1,11 @@
import { errorValue, errorValueToString } from "@quri/squiggle-lang"; import { SqError } from "@quri/squiggle-lang";
import React from "react"; import React from "react";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
type Props = { type Props = {
error: errorValue; error: SqError;
}; };
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => { export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
return <ErrorAlert heading="Error">{errorValueToString(error)}</ErrorAlert>; return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>;
}; };

View File

@ -24,7 +24,7 @@ import {
} from "@heroicons/react/solid"; } from "@heroicons/react/solid";
import clsx from "clsx"; import clsx from "clsx";
import { defaultBindings, environment } from "@quri/squiggle-lang"; import { environment } from "@quri/squiggle-lang";
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
@ -309,8 +309,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
executionId={executionId} executionId={executionId}
environment={env} environment={env}
{...vars} {...vars}
bindings={defaultBindings} // jsImports={imports}
jsImports={imports}
enableLocalSettings={true} enableLocalSettings={true}
/> />
</div> </div>

View File

@ -1,5 +1,5 @@
import React from "react"; import React, { useContext } from "react";
import { squiggleExpression, declaration } from "@quri/squiggle-lang"; import { SqDistributionTag, SqValue, SqValueTag } from "@quri/squiggle-lang";
import { NumberShower } from "../NumberShower"; import { NumberShower } from "../NumberShower";
import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart"; import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
import { FunctionChart, FunctionChartSettings } from "../FunctionChart"; import { FunctionChart, FunctionChartSettings } from "../FunctionChart";
@ -8,7 +8,10 @@ import { VariableBox } from "./VariableBox";
import { ItemSettingsMenu } from "./ItemSettingsMenu"; import { ItemSettingsMenu } from "./ItemSettingsMenu";
import { hasMassBelowZero } from "../../lib/distributionUtils"; import { hasMassBelowZero } from "../../lib/distributionUtils";
import { MergedItemSettings } from "./utils"; import { MergedItemSettings } from "./utils";
import { ViewerContext } from "./ViewerContext";
/*
// DISABLED FOR 0.4 branch, for now
function getRange<a>(x: declaration<a>) { function getRange<a>(x: declaration<a>) {
const first = x.args[0]; const first = x.args[0];
switch (first.tag) { switch (first.tag) {
@ -31,15 +34,21 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
count: 20, count: 20,
}; };
} }
*/
const VariableList: React.FC<{ const VariableList: React.FC<{
path: string[]; value: SqValue;
heading: string; heading: string;
children: (settings: MergedItemSettings) => React.ReactNode; children: (settings: MergedItemSettings) => React.ReactNode;
}> = ({ path, heading, children }) => ( }> = ({ value, heading, children }) => (
<VariableBox path={path} heading={heading}> <VariableBox value={value} heading={heading}>
{(settings) => ( {(settings) => (
<div className={clsx("space-y-3", path.length ? "pt-1 mt-1" : null)}> <div
className={clsx(
"space-y-3",
value.location.path.items.length ? "pt-1 mt-1" : null
)}
>
{children(settings)} {children(settings)}
</div> </div>
)} )}
@ -48,51 +57,44 @@ const VariableList: React.FC<{
export interface Props { export interface Props {
/** The output of squiggle's run */ /** The output of squiggle's run */
expression: squiggleExpression; value: SqValue;
/** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */
path: string[];
width?: number; width?: number;
} }
export const ExpressionViewer: React.FC<Props> = ({ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
path, const { getMergedSettings } = useContext(ViewerContext);
expression,
width, switch (value.tag) {
}) => { case SqValueTag.Number:
if (typeof expression !== "object") {
return (
<VariableList path={path} heading="Error">
{() => `Unknown expression: ${expression}`}
</VariableList>
);
}
switch (expression.tag) {
case "number":
return ( return (
<VariableBox path={path} heading="Number"> <VariableBox value={value} heading="Number">
{() => ( {() => (
<div className="font-semibold text-slate-600"> <div className="font-semibold text-slate-600">
<NumberShower precision={3} number={expression.value} /> <NumberShower precision={3} number={value.value} />
</div> </div>
)} )}
</VariableBox> </VariableBox>
); );
case "distribution": { case SqValueTag.Distribution: {
const distType = expression.value.type(); const distType = value.value.tag;
return ( return (
<VariableBox <VariableBox
path={path} value={value}
heading={`Distribution (${distType})\n${ heading={`Distribution (${distType})\n${
distType === "Symbolic" ? expression.value.toString() : "" distType === SqDistributionTag.Symbolic
? value.value.toString()
: ""
}`} }`}
renderSettingsMenu={({ onChange }) => { renderSettingsMenu={({ onChange }) => {
const shape = expression.value.pointSet(); const shape = value.value.pointSet(
getMergedSettings(value.location).environment
);
return ( return (
<ItemSettingsMenu <ItemSettingsMenu
path={path} value={value}
onChange={onChange} onChange={onChange}
disableLogX={ disableLogX={
shape.tag === "Ok" && hasMassBelowZero(shape.value) shape.tag === "Ok" && hasMassBelowZero(shape.value.asShape())
} }
withFunctionSettings={false} withFunctionSettings={false}
/> />
@ -102,7 +104,8 @@ export const ExpressionViewer: React.FC<Props> = ({
{(settings) => { {(settings) => {
return ( return (
<DistributionChart <DistributionChart
plot={defaultPlot(expression.value)} plot={defaultPlot(value.value)}
environment={settings.environment}
{...settings.distributionPlotSettings} {...settings.distributionPlotSettings}
height={settings.height} height={settings.height}
width={width} width={width}
@ -112,77 +115,77 @@ export const ExpressionViewer: React.FC<Props> = ({
</VariableBox> </VariableBox>
); );
} }
case "string": case SqValueTag.String:
return ( return (
<VariableBox path={path} heading="String"> <VariableBox value={value} heading="String">
{() => ( {() => (
<> <>
<span className="text-slate-400">"</span> <span className="text-slate-400">"</span>
<span className="text-slate-600 font-semibold font-mono"> <span className="text-slate-600 font-semibold font-mono">
{expression.value} {value.value}
</span> </span>
<span className="text-slate-400">"</span> <span className="text-slate-400">"</span>
</> </>
)} )}
</VariableBox> </VariableBox>
); );
case "boolean": case SqValueTag.Bool:
return ( return (
<VariableBox path={path} heading="Boolean"> <VariableBox value={value} heading="Boolean">
{() => expression.value.toString()} {() => value.value.toString()}
</VariableBox> </VariableBox>
); );
case "symbol": case SqValueTag.Symbol:
return ( return (
<VariableBox path={path} heading="Symbol"> <VariableBox value={value} heading="Symbol">
{() => ( {() => (
<> <>
<span className="text-slate-500 mr-2">Undefined Symbol:</span> <span className="text-slate-500 mr-2">Undefined Symbol:</span>
<span className="text-slate-600">{expression.value}</span> <span className="text-slate-600">{value.value}</span>
</> </>
)} )}
</VariableBox> </VariableBox>
); );
case "call": case SqValueTag.Call:
return ( return (
<VariableBox path={path} heading="Call"> <VariableBox value={value} heading="Call">
{() => expression.value} {() => value.value}
</VariableBox> </VariableBox>
); );
case "arraystring": case SqValueTag.ArrayString:
return ( return (
<VariableBox path={path} heading="Array String"> <VariableBox value={value} heading="Array String">
{() => expression.value.map((r) => `"${r}"`).join(", ")} {() => value.value.map((r) => `"${r}"`).join(", ")}
</VariableBox> </VariableBox>
); );
case "date": case SqValueTag.Date:
return ( return (
<VariableBox path={path} heading="Date"> <VariableBox value={value} heading="Date">
{() => expression.value.toDateString()} {() => value.value.toDateString()}
</VariableBox> </VariableBox>
); );
case "void": case SqValueTag.Void:
return ( return (
<VariableBox path={path} heading="Void"> <VariableBox value={value} heading="Void">
{() => "Void"} {() => "Void"}
</VariableBox> </VariableBox>
); );
case "timeDuration": { case SqValueTag.TimeDuration: {
return ( return (
<VariableBox path={path} heading="Time Duration"> <VariableBox value={value} heading="Time Duration">
{() => <NumberShower precision={3} number={expression.value} />} {() => <NumberShower precision={3} number={value.value} />}
</VariableBox> </VariableBox>
); );
} }
case "lambda": case SqValueTag.Lambda:
return ( return (
<VariableBox <VariableBox
path={path} value={value}
heading="Function" heading="Function"
renderSettingsMenu={({ onChange }) => { renderSettingsMenu={({ onChange }) => {
return ( return (
<ItemSettingsMenu <ItemSettingsMenu
path={path} value={value}
onChange={onChange} onChange={onChange}
withFunctionSettings={true} withFunctionSettings={true}
/> />
@ -191,11 +194,11 @@ export const ExpressionViewer: React.FC<Props> = ({
> >
{(settings) => ( {(settings) => (
<> <>
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value.parameters.join( <div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${value.value
"," .parameters()
)})`}</div> .join(",")})`}</div>
<FunctionChart <FunctionChart
fn={expression.value} fn={value.value}
chartSettings={settings.chartSettings} chartSettings={settings.chartSettings}
distributionPlotSettings={settings.distributionPlotSettings} distributionPlotSettings={settings.distributionPlotSettings}
height={settings.height} height={settings.height}
@ -208,47 +211,48 @@ export const ExpressionViewer: React.FC<Props> = ({
)} )}
</VariableBox> </VariableBox>
); );
case "lambdaDeclaration": { case SqValueTag.Declaration: {
return ( return (
<VariableBox <VariableBox
path={path} value={value}
heading="Function Declaration" heading="Function Declaration"
renderSettingsMenu={({ onChange }) => { renderSettingsMenu={({ onChange }) => {
return ( return (
<ItemSettingsMenu <ItemSettingsMenu
onChange={onChange} onChange={onChange}
path={path} value={value}
withFunctionSettings={true} withFunctionSettings={true}
/> />
); );
}} }}
> >
{(settings) => ( {(settings) => (
<FunctionChart <div>NOT IMPLEMENTED IN 0.4 YET</div>
fn={expression.value.fn} // <FunctionChart
chartSettings={getChartSettings(expression.value)} // fn={expression.value.fn}
distributionPlotSettings={settings.distributionPlotSettings} // chartSettings={getChartSettings(expression.value)}
height={settings.height} // distributionPlotSettings={settings.distributionPlotSettings}
environment={{ // height={settings.height}
sampleCount: settings.environment.sampleCount / 10, // environment={{
xyPointLength: settings.environment.xyPointLength / 10, // sampleCount: settings.environment.sampleCount / 10,
}} // xyPointLength: settings.environment.xyPointLength / 10,
/> // }}
// />
)} )}
</VariableBox> </VariableBox>
); );
} }
case "module": { case SqValueTag.Module: {
return ( return (
<VariableList path={path} heading="Module"> <VariableList value={value} heading="Module">
{(_) => {(_) =>
Object.entries(expression.value) value.value
.entries()
.filter(([key, _]) => !key.match(/^(Math|System)\./)) .filter(([key, _]) => !key.match(/^(Math|System)\./))
.map(([key, r]) => ( .map(([key, r]) => (
<ExpressionViewer <ExpressionViewer
key={key} key={key}
path={[...path, key]} value={r}
expression={r}
width={width !== undefined ? width - 20 : width} width={width !== undefined ? width - 20 : width}
/> />
)) ))
@ -256,23 +260,26 @@ export const ExpressionViewer: React.FC<Props> = ({
</VariableList> </VariableList>
); );
} }
case "record": case SqValueTag.Record:
const plot = makePlot(expression.value); const plot = makePlot(value.value);
if (plot) { if (plot) {
return ( return (
<VariableBox <VariableBox
path={path} value={value}
heading={"Plot"} heading="Plot"
renderSettingsMenu={({ onChange }) => { renderSettingsMenu={({ onChange }) => {
let disableLogX = plot.distributions.some((x) => { let disableLogX = plot.distributions.some((x) => {
let pointSet = x.distribution.pointSet(); let pointSet = x.distribution.pointSet(
getMergedSettings(value.location).environment
);
return ( return (
pointSet.tag === "Ok" && hasMassBelowZero(pointSet.value) pointSet.tag === "Ok" &&
hasMassBelowZero(pointSet.value.asShape())
); );
}); });
return ( return (
<ItemSettingsMenu <ItemSettingsMenu
path={path} value={value}
onChange={onChange} onChange={onChange}
disableLogX={disableLogX} disableLogX={disableLogX}
withFunctionSettings={false} withFunctionSettings={false}
@ -284,6 +291,7 @@ export const ExpressionViewer: React.FC<Props> = ({
return ( return (
<DistributionChart <DistributionChart
plot={plot} plot={plot}
environment={settings.environment}
{...settings.distributionPlotSettings} {...settings.distributionPlotSettings}
height={settings.height} height={settings.height}
width={width} width={width}
@ -294,44 +302,44 @@ export const ExpressionViewer: React.FC<Props> = ({
); );
} else { } else {
return ( return (
<VariableList path={path} heading="Record"> <VariableList value={value} heading="Record">
{(_) => {(_) =>
Object.entries(expression.value).map(([key, r]) => ( value.value
<ExpressionViewer .entries()
key={key} .map(([key, r]) => (
path={[...path, key]} <ExpressionViewer
expression={r} key={key}
width={width !== undefined ? width - 20 : width} value={r}
/> width={width !== undefined ? width - 20 : width}
)) />
))
} }
</VariableList> </VariableList>
); );
} }
case "array": case SqValueTag.Array:
return ( return (
<VariableList path={path} heading="Array"> <VariableList value={value} heading="Array">
{(_) => {(_) =>
expression.value.map((r, i) => ( value.value
<ExpressionViewer .getValues()
key={i} .map((r, i) => (
path={[...path, String(i)]} <ExpressionViewer
expression={r} key={i}
width={width !== undefined ? width - 20 : width} value={r}
/> width={width !== undefined ? width - 20 : width}
)) />
))
} }
</VariableList> </VariableList>
); );
default: { default: {
return ( return (
<VariableList path={path} heading="Error"> <VariableList value={value} heading="Error">
{() => ( {() => (
<div> <div>
<span>No display for type: </span>{" "} <span>No display for type: </span>{" "}
<span className="font-semibold text-slate-600"> <span className="font-semibold text-slate-600">{value.tag}</span>
{expression.tag}
</span>
</div> </div>
)} )}
</VariableList> </VariableList>

View File

@ -4,13 +4,14 @@ import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { Modal } from "../ui/Modal"; import { Modal } from "../ui/Modal";
import { ViewSettings, viewSettingsSchema } from "../ViewSettings"; import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
import { Path, pathAsString } from "./utils";
import { ViewerContext } from "./ViewerContext"; import { ViewerContext } from "./ViewerContext";
import { defaultTickFormat } from "../../lib/distributionSpecBuilder"; import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
import { PlaygroundContext } from "../SquigglePlayground"; import { PlaygroundContext } from "../SquigglePlayground";
import { SqValue } from "@quri/squiggle-lang";
import { locationAsString } from "./utils";
type Props = { type Props = {
path: Path; value: SqValue;
onChange: () => void; onChange: () => void;
disableLogX?: boolean; disableLogX?: boolean;
withFunctionSettings: boolean; withFunctionSettings: boolean;
@ -19,7 +20,7 @@ type Props = {
const ItemSettingsModal: React.FC< const ItemSettingsModal: React.FC<
Props & { close: () => void; resetScroll: () => void } Props & { close: () => void; resetScroll: () => void }
> = ({ > = ({
path, value,
onChange, onChange,
disableLogX, disableLogX,
withFunctionSettings, withFunctionSettings,
@ -29,7 +30,7 @@ const ItemSettingsModal: React.FC<
const { setSettings, getSettings, getMergedSettings } = const { setSettings, getSettings, getMergedSettings } =
useContext(ViewerContext); useContext(ViewerContext);
const mergedSettings = getMergedSettings(path); const mergedSettings = getMergedSettings(value.location);
const { register, watch } = useForm({ const { register, watch } = useForm({
resolver: yupResolver(viewSettingsSchema), resolver: yupResolver(viewSettingsSchema),
@ -53,8 +54,8 @@ const ItemSettingsModal: React.FC<
}); });
useEffect(() => { useEffect(() => {
const subscription = watch((vars) => { const subscription = watch((vars) => {
const settings = getSettings(path); // get the latest version const settings = getSettings(value.location); // get the latest version
setSettings(path, { setSettings(value.location, {
...settings, ...settings,
distributionPlotSettings: { distributionPlotSettings: {
showSummary: vars.showSummary, showSummary: vars.showSummary,
@ -75,7 +76,7 @@ const ItemSettingsModal: React.FC<
onChange(); onChange();
}); });
return () => subscription.unsubscribe(); return () => subscription.unsubscribe();
}, [getSettings, setSettings, onChange, path, watch]); }, [getSettings, setSettings, onChange, value.location, watch]);
const { getLeftPanelElement } = useContext(PlaygroundContext); const { getLeftPanelElement } = useContext(PlaygroundContext);
@ -83,7 +84,7 @@ const ItemSettingsModal: React.FC<
<Modal container={getLeftPanelElement()} close={close}> <Modal container={getLeftPanelElement()} close={close}>
<Modal.Header> <Modal.Header>
Chart settings Chart settings
{path.length ? ( {value.location.path.items.length ? (
<> <>
{" for "} {" for "}
<span <span
@ -91,7 +92,7 @@ const ItemSettingsModal: React.FC<
className="cursor-pointer" className="cursor-pointer"
onClick={resetScroll} onClick={resetScroll}
> >
{pathAsString(path)} {locationAsString(value.location)}
</span>{" "} </span>{" "}
</> </>
) : ( ) : (
@ -120,7 +121,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
if (!enableLocalSettings) { if (!enableLocalSettings) {
return null; return null;
} }
const settings = getSettings(props.path); const settings = getSettings(props.value.location);
const resetScroll = () => { const resetScroll = () => {
if (!ref.current) return; if (!ref.current) return;
@ -139,7 +140,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
{settings.distributionPlotSettings || settings.chartSettings ? ( {settings.distributionPlotSettings || settings.chartSettings ? (
<button <button
onClick={() => { onClick={() => {
setSettings(props.path, { setSettings(props.value.location, {
...settings, ...settings,
distributionPlotSettings: undefined, distributionPlotSettings: undefined,
chartSettings: undefined, chartSettings: undefined,

View File

@ -1,3 +1,4 @@
import { SqValue, SqValueLocation } from "@quri/squiggle-lang";
import React, { useContext, useReducer } from "react"; import React, { useContext, useReducer } from "react";
import { Tooltip } from "../ui/Tooltip"; import { Tooltip } from "../ui/Tooltip";
import { LocalItemSettings, MergedItemSettings } from "./utils"; import { LocalItemSettings, MergedItemSettings } from "./utils";
@ -8,14 +9,14 @@ type SettingsMenuParams = {
}; };
type VariableBoxProps = { type VariableBoxProps = {
path: string[]; value: SqValue;
heading: string; heading: string;
renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode; renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode;
children: (settings: MergedItemSettings) => React.ReactNode; children: (settings: MergedItemSettings) => React.ReactNode;
}; };
export const VariableBox: React.FC<VariableBoxProps> = ({ export const VariableBox: React.FC<VariableBoxProps> = ({
path, value: { location },
heading = "Error", heading = "Error",
renderSettingsMenu, renderSettingsMenu,
children, children,
@ -27,10 +28,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
// So we use `forceUpdate` to force rerendering. // So we use `forceUpdate` to force rerendering.
const [_, forceUpdate] = useReducer((x) => x + 1, 0); const [_, forceUpdate] = useReducer((x) => x + 1, 0);
const settings = getSettings(path); const settings = getSettings(location);
const setSettingsAndUpdate = (newSettings: LocalItemSettings) => { const setSettingsAndUpdate = (newSettings: LocalItemSettings) => {
setSettings(path, newSettings); setSettings(location, newSettings);
forceUpdate(); forceUpdate();
}; };
@ -38,8 +39,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed }); setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed });
}; };
const isTopLevel = path.length === 0; const isTopLevel = location.path.items.length === 0;
const name = isTopLevel ? "Result" : path[path.length - 1]; const name = isTopLevel
? { result: "Result", bindings: "Bindings" }[location.path.root]
: location.path.items[location.path.items.length - 1];
return ( return (
<div> <div>
@ -65,13 +68,13 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
</header> </header>
{settings.collapsed ? null : ( {settings.collapsed ? null : (
<div className="flex w-full"> <div className="flex w-full">
{path.length ? ( {location.path.items.length ? (
<div <div
className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer" className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
onClick={toggleCollapsed} onClick={toggleCollapsed}
></div> ></div>
) : null} ) : null}
<div className="grow">{children(getMergedSettings(path))}</div> <div className="grow">{children(getMergedSettings(location))}</div>
</div> </div>
)} )}
</div> </div>

View File

@ -1,14 +1,14 @@
import { defaultEnvironment } from "@quri/squiggle-lang"; import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
import React from "react"; import React from "react";
import { LocalItemSettings, MergedItemSettings, Path } from "./utils"; import { LocalItemSettings, MergedItemSettings } from "./utils";
type ViewerContextShape = { type ViewerContextShape = {
// Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update). // Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
// Instead, we keep settings in local state and notify the global context via setSettings to pass them down the component tree again if it got rebuilt from scratch. // Instead, we keep settings in local state and notify the global context via setSettings to pass them down the component tree again if it got rebuilt from scratch.
// See ./SquiggleViewer.tsx and ./VariableBox.tsx for other implementation details on this. // See ./SquiggleViewer.tsx and ./VariableBox.tsx for other implementation details on this.
getSettings(path: Path): LocalItemSettings; getSettings(location: SqValueLocation): LocalItemSettings;
getMergedSettings(path: Path): MergedItemSettings; getMergedSettings(location: SqValueLocation): MergedItemSettings;
setSettings(path: Path, value: LocalItemSettings): void; setSettings(location: SqValueLocation, value: LocalItemSettings): void;
enableLocalSettings: boolean; // show local settings icon in the UI enableLocalSettings: boolean; // show local settings icon in the UI
}; };

View File

@ -1,21 +1,20 @@
import React, { useCallback, useRef } from "react"; import React, { useCallback, useRef } from "react";
import { environment } from "@quri/squiggle-lang"; import { environment, SqValueLocation } from "@quri/squiggle-lang";
import { DistributionPlottingSettings } from "../DistributionChart"; import { DistributionPlottingSettings } from "../DistributionChart";
import { FunctionChartSettings } from "../FunctionChart"; import { FunctionChartSettings } from "../FunctionChart";
import { ExpressionViewer } from "./ExpressionViewer"; import { ExpressionViewer } from "./ExpressionViewer";
import { ViewerContext } from "./ViewerContext"; import { ViewerContext } from "./ViewerContext";
import { import {
LocalItemSettings, LocalItemSettings,
locationAsString,
MergedItemSettings, MergedItemSettings,
Path,
pathAsString,
} from "./utils"; } from "./utils";
import { useSquiggle } from "../../lib/hooks"; import { useSquiggle } from "../../lib/hooks";
import { SquiggleErrorAlert } from "../SquiggleErrorAlert"; import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
type Props = { type Props = {
/** The output of squiggle's run */ /** The output of squiggle's run */
result: ReturnType<typeof useSquiggle>; result: ReturnType<typeof useSquiggle>["result"];
width?: number; width?: number;
height: number; height: number;
distributionPlotSettings: DistributionPlottingSettings; distributionPlotSettings: DistributionPlottingSettings;
@ -45,22 +44,22 @@ export const SquiggleViewer: React.FC<Props> = ({
const settingsRef = useRef<Settings>({}); const settingsRef = useRef<Settings>({});
const getSettings = useCallback( const getSettings = useCallback(
(path: Path) => { (location: SqValueLocation) => {
return settingsRef.current[pathAsString(path)] || defaultSettings; return settingsRef.current[locationAsString(location)] || defaultSettings;
}, },
[settingsRef] [settingsRef]
); );
const setSettings = useCallback( const setSettings = useCallback(
(path: Path, value: LocalItemSettings) => { (location: SqValueLocation, value: LocalItemSettings) => {
settingsRef.current[pathAsString(path)] = value; settingsRef.current[locationAsString(location)] = value;
}, },
[settingsRef] [settingsRef]
); );
const getMergedSettings = useCallback( const getMergedSettings = useCallback(
(path: Path) => { (location: SqValueLocation) => {
const localSettings = getSettings(path); const localSettings = getSettings(location);
const result: MergedItemSettings = { const result: MergedItemSettings = {
distributionPlotSettings: { distributionPlotSettings: {
...distributionPlotSettings, ...distributionPlotSettings,
@ -91,7 +90,7 @@ export const SquiggleViewer: React.FC<Props> = ({
}} }}
> >
{result.tag === "Ok" ? ( {result.tag === "Ok" ? (
<ExpressionViewer path={[]} expression={result.value} width={width} /> <ExpressionViewer value={result.value} width={width} />
) : ( ) : (
<SquiggleErrorAlert error={result.value} /> <SquiggleErrorAlert error={result.value} />
)} )}

View File

@ -1,6 +1,6 @@
import { DistributionPlottingSettings } from "../DistributionChart"; import { DistributionPlottingSettings } from "../DistributionChart";
import { FunctionChartSettings } from "../FunctionChart"; import { FunctionChartSettings } from "../FunctionChart";
import { environment } from "@quri/squiggle-lang"; import { environment, SqValueLocation } from "@quri/squiggle-lang";
export type LocalItemSettings = { export type LocalItemSettings = {
collapsed: boolean; collapsed: boolean;
@ -17,6 +17,5 @@ export type MergedItemSettings = {
environment: environment; environment: environment;
}; };
export type Path = string[]; export const locationAsString = (location: SqValueLocation) =>
location.path.items.join(".");
export const pathAsString = (path: Path) => path.join(".");

View File

@ -1,7 +1,4 @@
export { SquiggleChart } from "./components/SquiggleChart"; export { SquiggleChart } from "./components/SquiggleChart";
export { SquiggleEditor, SquigglePartial } from "./components/SquiggleEditor"; export { SquiggleEditor } from "./components/SquiggleEditor";
export { SquigglePlayground } from "./components/SquigglePlayground"; export { SquigglePlayground } from "./components/SquigglePlayground";
export { SquiggleContainer } from "./components/SquiggleContainer"; export { SquiggleContainer } from "./components/SquiggleContainer";
export { SquiggleEditorWithImportedBindings } from "./components/SquiggleEditorWithImportedBindings";
export { mergeBindings } from "@quri/squiggle-lang";

View File

@ -1,5 +1,5 @@
import { shape } from "@quri/squiggle-lang"; import { SqShape } from "@quri/squiggle-lang";
export const hasMassBelowZero = (shape: shape) => export const hasMassBelowZero = (shape: SqShape) =>
shape.continuous.some((x) => x.x <= 0) || shape.continuous.some((x) => x.x <= 0) ||
shape.discrete.some((x) => x.x <= 0); shape.discrete.some((x) => x.x <= 0);

View File

@ -1,3 +1,3 @@
export { useMaybeControlledValue } from "./useMaybeControlledValue"; export { useMaybeControlledValue } from "./useMaybeControlledValue";
export { useSquiggle, useSquigglePartial } from "./useSquiggle"; export { useSquiggle } from "./useSquiggle";
export { useRunnerState } from "./useRunnerState"; export { useRunnerState } from "./useRunnerState";

View File

@ -1,34 +1,27 @@
import { import { environment, run, SqValue } from "@quri/squiggle-lang";
bindings,
environment,
jsImports,
run,
runPartial,
} from "@quri/squiggle-lang";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = { type SquiggleArgs = {
code: string; code: string;
executionId?: number; executionId?: number;
bindings?: bindings; // jsImports?: jsImports;
jsImports?: jsImports;
environment?: environment; environment?: environment;
onChange?: (expr: Extract<T, { tag: "Ok" }>["value"] | undefined) => void; onChange?: (expr: SqValue | undefined) => void;
}; };
const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>( export const useSquiggle = (args: SquiggleArgs) => {
args: SquiggleArgs<T>, const result = useMemo(
f: (...args: Parameters<typeof run>) => T () => {
) => { const result = run(args.code, {
const result: T = useMemo<T>( environment: args.environment,
() => f(args.code, args.bindings, args.environment, args.jsImports), });
return result;
},
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[ [
f,
args.code, args.code,
args.bindings,
args.environment, args.environment,
args.jsImports, // args.jsImports,
args.executionId, args.executionId,
] ]
); );
@ -36,18 +29,8 @@ const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>(
const { onChange } = args; const { onChange } = args;
useEffect(() => { useEffect(() => {
onChange?.(result.tag === "Ok" ? result.value : undefined); onChange?.(result.result.tag === "Ok" ? result.result.value : undefined);
}, [result, onChange]); }, [result, onChange]);
return result; return result;
}; };
export const useSquigglePartial = (
args: SquiggleArgs<ReturnType<typeof runPartial>>
) => {
return useSquiggleAny(args, runPartial);
};
export const useSquiggle = (args: SquiggleArgs<ReturnType<typeof run>>) => {
return useSquiggleAny(args, run);
};

View File

@ -1,9 +1,9 @@
import * as yup from "yup"; import * as yup from "yup";
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang"; import { SqDistribution, result, SqRecord } from "@quri/squiggle-lang";
export type LabeledDistribution = { export type LabeledDistribution = {
name: string; name: string;
distribution: Distribution; distribution: SqDistribution;
color?: string; color?: string;
}; };
@ -53,9 +53,7 @@ const schema = yup
}), }),
}); });
export function parsePlot(record: { export function parsePlot(record: SqRecord): result<Plot, string> {
[key: string]: squiggleExpression;
}): result<Plot, string> {
try { try {
const plotRecord = schema.validateSync(record); const plotRecord = schema.validateSync(record);
return ok({ return ok({

View File

@ -1,10 +1,5 @@
import { import { run, SqProject, SqValue, SqValueTag } from "../../src/js";
Distribution, import { testRun } from "./TestHelpers";
resultMap,
defaultBindings,
mergeBindings,
} from "../../src/js/index";
import { testRun, testRunPartial } from "./TestHelpers";
function Ok<b>(x: b) { function Ok<b>(x: b) {
return { tag: "Ok", value: x }; return { tag: "Ok", value: x };
@ -12,97 +7,57 @@ function Ok<b>(x: b) {
describe("Simple calculations and results", () => { describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => { test("mean(normal(5,2))", () => {
expect(testRun("mean(normal(5,2))")).toEqual({ const result = testRun("mean(normal(5,2))"); // FIXME
tag: "number", expect(result.toString()).toEqual("5");
value: 5,
});
}); });
test("10+10", () => { test("10+10", () => {
let foo = testRun("10 + 10"); let result = testRun("10 + 10");
expect(foo).toEqual({ tag: "number", value: 20 }); expect(result.toString()).toEqual("20");
}); });
}); });
describe("Log function", () => { describe("Log function", () => {
test("log(1) = 0", () => { test("log(1) = 0", () => {
let foo = testRun("log(1)"); let foo = testRun("log(1)");
expect(foo).toEqual({ tag: "number", value: 0 }); expect(foo.toString()).toEqual("0");
}); });
}); });
describe("Array", () => { describe("Array", () => {
test("nested Array", () => { test("nested Array", () => {
expect(testRun("[[1]]")).toEqual({ expect(testRun("[[ 1 ]]").toString()).toEqual("[[1]]");
tag: "array",
value: [
{
tag: "array",
value: [
{
tag: "number",
value: 1,
},
],
},
],
});
}); });
}); });
describe("Record", () => { describe("Record", () => {
test("Return record", () => { test("Return record", () => {
expect(testRun("{a: 1}")).toEqual({ expect(testRun("{a:1}").toString()).toEqual("{a: 1}");
tag: "record",
value: {
a: {
tag: "number",
value: 1,
},
},
});
}); });
}); });
describe("Partials", () => { describe("Continues", () => {
test("Can pass variables between partials and cells", () => { test("Bindings from continues are accessible", () => {
let bindings = testRunPartial(`x = 5`); const project = SqProject.create();
let bindings2 = testRunPartial(`y = x + 2`, bindings); project.setSource("p1", "x = 5");
expect(testRun(`y + 3`, bindings2)).toEqual({ project.setSource("p2", "y = x + 2");
tag: "number", project.setSource("main", "y + 3");
value: 10, project.setContinues("main", ["p2"]);
}); project.setContinues("p2", ["p1"]);
project.run("main");
const result = project.getResult("main");
expect(result.tag).toEqual("Ok");
expect(result.value.toString()).toEqual("10");
}); });
test("Can merge bindings from three partials", () => { test("Can merge bindings from three partials", () => {
let bindings1 = testRunPartial(`x = 1`); const project = SqProject.create();
let bindings2 = testRunPartial(`y = 2`); project.setSource("p1", "x = 1");
let bindings3 = testRunPartial(`z = 3`); project.setSource("p2", "y = 2");
expect( project.setSource("p3", "z = 3");
testRun(`x + y + z`, mergeBindings([bindings1, bindings2, bindings3])) project.setSource("main", "x + y + z");
).toEqual({ project.setContinues("main", ["p1", "p2", "p3"]);
tag: "number", project.run("main");
value: 6, const result = project.getResult("main");
}); expect(result.tag).toEqual("Ok");
}); expect(result.value.toString()).toEqual("6");
});
describe("JS Imports", () => {
test("Can pass parameters into partials and cells", () => {
let bindings = testRunPartial(`y = $x + 2`, defaultBindings, { x: 1 });
let bindings2 = testRunPartial(`z = y + $a`, bindings, { a: 3 });
expect(testRun(`z`, bindings2)).toEqual({
tag: "number",
value: 6,
});
});
test("Complicated deep parameters", () => {
expect(
testRun(`$x.y[0][0].w + $x.z + $u.v`, defaultBindings, {
x: { y: [[{ w: 1 }]], z: 2 },
u: { v: 3 },
})
).toEqual({
tag: "number",
value: 6,
});
}); });
}); });
@ -112,50 +67,62 @@ describe("Distribution", () => {
let env = { sampleCount: 8, xyPointLength: 100 }; let env = { sampleCount: 8, xyPointLength: 100 };
let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30]; let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30];
let dist1SampleCount = dist1Samples.length; let dist1SampleCount = dist1Samples.length;
let dist = new Distribution(
{ tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] }, const buildDist = (samples: number[]) => {
env const src = `SampleSet.fromList([${samples.join(",")}])`;
); const { result } = run(src, {
let dist2 = new Distribution( environment: env,
{ tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] }, });
env if (result.tag !== "Ok") {
); throw new Error(
`Failed to build SampleSet: from ${src}: ${result.value}`
);
}
const dist = result.value;
if (dist.tag !== SqValueTag.Distribution) {
throw new Error("Expected Distribution");
}
return dist.value;
};
const dist = buildDist(dist1Samples);
const dist2 = buildDist([20, 22, 24, 29, 30, 35, 38, 44, 52]);
test("mean", () => { test("mean", () => {
expect(dist.mean().value).toBeCloseTo(9.5555555); expect(dist.mean(env).value).toBeCloseTo(9.5555555);
}); });
test("pdf", () => { test("pdf", () => {
expect(dist.pdf(5.0).value).toBeCloseTo(0.10499097598222966, 1); expect(dist.pdf(env, 5.0).value).toBeCloseTo(0.10499097598222966, 1);
}); });
test("cdf", () => { test("cdf", () => {
expect(dist.cdf(5.0).value).toBeCloseTo( expect(dist.cdf(env, 5.0).value).toBeCloseTo(
dist1Samples.filter((x) => x <= 5).length / dist1SampleCount, dist1Samples.filter((x) => x <= 5).length / dist1SampleCount,
1 1
); );
}); });
test("inv", () => { test("inv", () => {
expect(dist.inv(0.5).value).toBeCloseTo(6); expect(dist.inv(env, 0.5).value).toBeCloseTo(6);
});
test("toPointSet", () => {
expect(
resultMap(dist.toPointSet(), (r: Distribution) => r.toString())
).toEqual(Ok("Point Set Distribution"));
});
test("toSparkline", () => {
expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁");
});
test("algebraicAdd", () => {
expect(
resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
r.toSparkline(20)
).value
).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
});
test("pointwiseAdd", () => {
expect(
resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
r.toSparkline(20)
).value
).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁"));
}); });
// test("toPointSet", () => {
// expect(
// resultMap(dist.toPointSet(), (r: Distribution) => r.toString())
// ).toEqual(Ok("Point Set Distribution"));
// });
// test("toSparkline", () => {
// expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁");
// });
// test("algebraicAdd", () => {
// expect(
// resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
// r.toSparkline(20)
// ).value
// ).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
// });
// test("pointwiseAdd", () => {
// expect(
// resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
// r.toSparkline(20)
// ).value
// ).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁"));
// });
}); });

View File

@ -1,4 +1,3 @@
// import { errorValueToString } from "../../src/js/index";
import * as fc from "fast-check"; import * as fc from "fast-check";
import { testRun } from "./TestHelpers"; import { testRun } from "./TestHelpers";

View File

@ -1,9 +1,3 @@
import {
run,
squiggleExpression,
errorValue,
result,
} from "../../src/js/index";
import { testRun } from "./TestHelpers"; import { testRun } from "./TestHelpers";
import * as fc from "fast-check"; import * as fc from "fast-check";
@ -42,9 +36,9 @@ describe("Squiggle's parser is whitespace insensitive", () => {
whitespaceGen(), whitespaceGen(),
whitespaceGen(), whitespaceGen(),
(a, b, c, d, e, f, g, h) => { (a, b, c, d, e, f, g, h) => {
expect(testRun(squiggleString(a, b, c, d, e, f, g, h))).toEqual( expect(
squiggleOutput testRun(squiggleString(a, b, c, d, e, f, g, h))
); ).toEqualSqValue(squiggleOutput);
} }
) )
); );

View File

@ -1,5 +1,4 @@
// import { errorValueToString } from "../../src/js/index"; import { testRun, expectErrorToBeBounded, SqValueTag } from "./TestHelpers";
import { testRun, expectErrorToBeBounded } from "./TestHelpers";
import * as fc from "fast-check"; import * as fc from "fast-check";
describe("Mean of mixture is weighted average of means", () => { describe("Mean of mixture is weighted average of means", () => {
@ -20,7 +19,7 @@ describe("Mean of mixture is weighted average of means", () => {
let lognormalWeight = y / weightDenom; let lognormalWeight = y / weightDenom;
let betaMean = 1 / (1 + b / a); let betaMean = 1 / (1 + b / a);
let lognormalMean = m + s ** 2 / 2; let lognormalMean = m + s ** 2 / 2;
if (res.tag == "number") { if (res.tag === SqValueTag.Number) {
expectErrorToBeBounded( expectErrorToBeBounded(
res.value, res.value,
betaWeight * betaMean + lognormalWeight * lognormalMean, betaWeight * betaMean + lognormalWeight * lognormalMean,

View File

@ -1,12 +1,13 @@
import { Distribution } from "../../src/js/index"; import { expectErrorToBeBounded, testRun, SqValueTag } from "./TestHelpers";
import { expectErrorToBeBounded, failDefault, testRun } from "./TestHelpers";
import * as fc from "fast-check"; import * as fc from "fast-check";
// Beware: float64Array makes it appear in an infinite loop. // Beware: float64Array makes it appear in an infinite loop.
let arrayGen = () => let arrayGen = () =>
fc fc
.float32Array({ .float64Array({
minLength: 10, minLength: 10,
max: 999999999999999,
min: -999999999999999,
maxLength: 10000, maxLength: 10000,
noDefaultInfinity: true, noDefaultInfinity: true,
noNaN: true, noNaN: true,
@ -14,36 +15,41 @@ let arrayGen = () =>
.filter( .filter(
(xs_) => Math.min(...Array.from(xs_)) != Math.max(...Array.from(xs_)) (xs_) => Math.min(...Array.from(xs_)) != Math.max(...Array.from(xs_))
); );
describe("cumulative density function", () => {
let n = 10000;
let makeSampleSet = (samples: number[]) => {
let sampleList = samples.map((x) => x.toFixed(20)).join(",");
let result = testRun(`SampleSet.fromList([${sampleList}])`);
if (result.tag === SqValueTag.Distribution) {
return result.value;
} else {
fail("Expected to be distribution");
}
};
const env = { sampleCount: 10000, xyPointLength: 100 };
describe("cumulative density function", () => {
// We should fix this. // We should fix this.
test.skip("'s codomain is bounded above", () => { test.skip("'s codomain is bounded above", () => {
fc.assert( fc.assert(
fc.property(arrayGen(), fc.float(), (xs_, x) => { fc.property(arrayGen(), fc.float(), (xs_, x) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
// Should compute with squiggle strings once interpreter has `sample` // Should compute with squiggle strings once interpreter has `sample`
let dist = new Distribution( let result = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = result.cdf(env, x).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(x).value;
let epsilon = 5e-7; let epsilon = 5e-7;
expect(cdfValue).toBeLessThanOrEqual(1 + epsilon); expect(cdfValue).toBeLessThanOrEqual(1 + epsilon);
}) })
); );
}); });
test("'s codomain is bounded below", () => { test.skip("'s codomain is bounded below", () => {
fc.assert( fc.assert(
fc.property(arrayGen(), fc.float(), (xs_, x) => { fc.property(arrayGen(), fc.float(), (xs_, x) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
// Should compute with squiggle strings once interpreter has `sample` // Should compute with squiggle strings once interpreter has `sample`
let dist = new Distribution( let result = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = result.cdf(env, x).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(x).value;
expect(cdfValue).toBeGreaterThanOrEqual(0); expect(cdfValue).toBeGreaterThanOrEqual(0);
}) })
); );
@ -57,11 +63,8 @@ describe("cumulative density function", () => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let max = Math.max(...xs); let max = Math.max(...xs);
// Should compute with squiggle strings once interpreter has `sample` // Should compute with squiggle strings once interpreter has `sample`
let dist = new Distribution( let result = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = result.cdf(env, max).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(max).value;
expect(cdfValue).toBeCloseTo(1.0, 2); expect(cdfValue).toBeCloseTo(1.0, 2);
}) })
); );
@ -74,11 +77,8 @@ describe("cumulative density function", () => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let min = Math.min(...xs); let min = Math.min(...xs);
// Should compute with squiggle strings once interpreter has `sample` // Should compute with squiggle strings once interpreter has `sample`
let dist = new Distribution( let result = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = result.cdf(env, min).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(min).value;
let max = Math.max(...xs); let max = Math.max(...xs);
let epsilon = 5e-3; let epsilon = 5e-3;
if (max - min < epsilon) { if (max - min < epsilon) {
@ -95,11 +95,8 @@ describe("cumulative density function", () => {
fc.assert( fc.assert(
fc.property(arrayGen(), fc.float(), (xs_, x) => { fc.property(arrayGen(), fc.float(), (xs_, x) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let dist = new Distribution( let dist = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = dist.cdf(env, x).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(x).value;
let max = Math.max(...xs); let max = Math.max(...xs);
if (x > max) { if (x > max) {
let epsilon = (x - max) / x; let epsilon = (x - max) / x;
@ -107,21 +104,18 @@ describe("cumulative density function", () => {
} else if (typeof cdfValue == "number") { } else if (typeof cdfValue == "number") {
expect(Math.round(1e5 * cdfValue) / 1e5).toBeLessThanOrEqual(1); expect(Math.round(1e5 * cdfValue) / 1e5).toBeLessThanOrEqual(1);
} else { } else {
failDefault(); fail();
} }
}) })
); );
}); });
test("is non-negative everywhere with zero when x is lower than the min", () => { test.skip("is non-negative everywhere with zero when x is lower than the min", () => {
fc.assert( fc.assert(
fc.property(arrayGen(), fc.float(), (xs_, x) => { fc.property(arrayGen(), fc.float(), (xs_, x) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let dist = new Distribution( let dist = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let cdfValue = dist.cdf(env, x).value;
{ sampleCount: n, xyPointLength: 100 }
);
let cdfValue = dist.cdf(x).value;
expect(cdfValue).toBeGreaterThanOrEqual(0); expect(cdfValue).toBeGreaterThanOrEqual(0);
}) })
); );
@ -130,7 +124,7 @@ describe("cumulative density function", () => {
// I no longer believe this is true. // I no longer believe this is true.
describe("probability density function", () => { describe("probability density function", () => {
let n = 1000; const env = { sampleCount: 1000, xyPointLength: 100 };
test.skip("assigns to the max at most the weight of the mean", () => { test.skip("assigns to the max at most the weight of the mean", () => {
fc.assert( fc.assert(
@ -139,12 +133,9 @@ describe("probability density function", () => {
let max = Math.max(...xs); let max = Math.max(...xs);
let mean = xs.reduce((a, b) => a + b, 0.0) / xs.length; let mean = xs.reduce((a, b) => a + b, 0.0) / xs.length;
// Should be from squiggleString once interpreter exposes sampleset // Should be from squiggleString once interpreter exposes sampleset
let dist = new Distribution( let dist = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let pdfValueMean = dist.pdf(env, mean).value;
{ sampleCount: n, xyPointLength: 100 } let pdfValueMax = dist.pdf(env, max).value;
);
let pdfValueMean = dist.pdf(mean).value;
let pdfValueMax = dist.pdf(max).value;
if (typeof pdfValueMean == "number" && typeof pdfValueMax == "number") { if (typeof pdfValueMean == "number" && typeof pdfValueMax == "number") {
expect(pdfValueMax).toBeLessThanOrEqual(pdfValueMean); expect(pdfValueMax).toBeLessThanOrEqual(pdfValueMean);
} else { } else {
@ -164,11 +155,9 @@ describe("mean is mean", () => {
(xs_) => { (xs_) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let n = xs.length; let n = xs.length;
let dist = new Distribution( let dist = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let myEnv = { sampleCount: 2 * n, xyPointLength: 4 * n };
{ sampleCount: 2 * n, xyPointLength: 4 * n } let mean = dist.mean(myEnv);
);
let mean = dist.mean();
if (typeof mean.value == "number") { if (typeof mean.value == "number") {
expectErrorToBeBounded( expectErrorToBeBounded(
mean.value, mean.value,
@ -177,7 +166,7 @@ describe("mean is mean", () => {
1 1
); );
} else { } else {
failDefault(); fail();
} }
} }
) )
@ -191,11 +180,9 @@ describe("mean is mean", () => {
(xs_) => { (xs_) => {
let xs = Array.from(xs_); let xs = Array.from(xs_);
let n = xs.length; let n = xs.length;
let dist = new Distribution( let dist = makeSampleSet(xs);
{ tag: "SampleSet", value: xs }, let myEnv = { sampleCount: Math.floor(n / 2), xyPointLength: 4 * n };
{ sampleCount: Math.floor(n / 2), xyPointLength: 4 * n } let mean = dist.mean(myEnv);
);
let mean = dist.mean();
if (typeof mean.value == "number") { if (typeof mean.value == "number") {
expectErrorToBeBounded( expectErrorToBeBounded(
mean.value, mean.value,
@ -204,7 +191,7 @@ describe("mean is mean", () => {
1 1
); );
} else { } else {
failDefault(); fail();
} }
} }
) )

View File

@ -5,7 +5,7 @@ import * as fc from "fast-check";
describe("Scalar manipulation is well-modeled by javascript math", () => { describe("Scalar manipulation is well-modeled by javascript math", () => {
test("in the case of natural logarithms", () => { test("in the case of natural logarithms", () => {
fc.assert( fc.assert(
fc.property(fc.integer(), (x) => { fc.property(fc.nat(), (x) => {
let squiggleString = `log(${x})`; let squiggleString = `log(${x})`;
let squiggleResult = testRun(squiggleString); let squiggleResult = testRun(squiggleString);
if (x == 0) { if (x == 0) {

View File

@ -1,68 +1,31 @@
import { import { run, SqValueTag } from "../../src/js";
run, export { SqValueTag };
runPartial,
bindings,
squiggleExpression,
errorValueToString,
defaultImports,
defaultBindings,
jsImports,
} from "../../src/js/index";
export function testRun( expect.extend({
x: string, toEqualSqValue(x, y) {
bindings: bindings = defaultBindings, // hack via https://github.com/facebook/jest/issues/10329#issuecomment-820656061
imports: jsImports = defaultImports const { getMatchers } = require("expect/build/jestMatchersObject");
): squiggleExpression { return getMatchers().toEqual.call(this, x.toString(), y.toString());
let squiggleResult = run( },
x, });
bindings,
{ export function testRun(x: string) {
const { result, bindings } = run(x, {
environment: {
sampleCount: 1000, sampleCount: 1000,
xyPointLength: 100, xyPointLength: 100,
}, },
imports });
);
if (squiggleResult.tag === "Ok") { if (result.tag === "Ok") {
return squiggleResult.value; return result.value;
} else { } else {
throw new Error( throw new Error(
`Expected squiggle expression to evaluate but got error: ${errorValueToString( `Expected squiggle expression to evaluate but got error: ${result.value}`
squiggleResult.value
)}`
); );
} }
} }
export function testRunPartial(
x: string,
bindings: bindings = defaultBindings,
imports: jsImports = defaultImports
): bindings {
let squiggleResult = runPartial(
x,
bindings,
{
sampleCount: 1000,
xyPointLength: 100,
},
imports
);
if (squiggleResult.tag === "Ok") {
return squiggleResult.value;
} else {
throw new Error(
`Expected squiggle expression to evaluate but got error: ${errorValueToString(
squiggleResult.value
)}`
);
}
}
export function failDefault() {
expect("be reached").toBe("codepath should never");
}
/** /**
* This appears also in `TestHelpers.res`. According to https://www.math.net/percent-error, it computes * This appears also in `TestHelpers.res`. According to https://www.math.net/percent-error, it computes
* absolute error when numerical stability concerns make me not want to compute relative error. * absolute error when numerical stability concerns make me not want to compute relative error.

View File

@ -0,0 +1,15 @@
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
import { wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
type T = RSArray.squiggleValue_Array;
export class SqArray {
constructor(private _value: T, public location: SqValueLocation) {}
getValues() {
return RSArray.getValues(this._value).map((v, i) =>
wrapValue(v, this.location.extend(i))
);
}
}

View File

@ -0,0 +1,115 @@
import * as RSDistribution from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
import { distributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_tag";
import { environment } from "../rescript/ForTS/ForTS__Types.gen";
import { SqDistributionError } from "./SqDistributionError";
import { wrapPointSetDist } from "./SqPointSetDist";
import { resultMap2 } from "./types";
type T = RSDistribution.distribution;
export { Tag as SqDistributionTag };
export const wrapDistribution = (value: T): SqDistribution => {
const tag = RSDistribution.getTag(value);
return new tagToClass[tag](value);
};
abstract class SqAbstractDistribution {
abstract tag: Tag;
constructor(private _value: T) {}
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
const value = rsMethod(this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
pointSet(env: environment) {
const innerResult = RSDistribution.toPointSet(this._value, env);
return resultMap2(
innerResult,
wrapPointSetDist,
(v: RSDistribution.distributionError) => new SqDistributionError(v)
);
}
toString() {
RSDistribution.toString(this._value);
}
mean(env: environment) {
return resultMap2(
RSDistribution.mean({ env }, this._value),
(v: number) => v,
(e: RSDistribution.distributionError) => new SqDistributionError(e)
);
}
pdf(env: environment, n: number) {
return resultMap2(
RSDistribution.pdf({ env }, this._value, n),
(v: number) => v,
(e: RSDistribution.distributionError) => new SqDistributionError(e)
);
}
cdf(env: environment, n: number) {
return resultMap2(
RSDistribution.cdf({ env }, this._value, n),
(v: number) => v,
(e: RSDistribution.distributionError) => new SqDistributionError(e)
);
}
inv(env: environment, n: number) {
return resultMap2(
RSDistribution.inv({ env }, this._value, n),
(v: number) => v,
(e: RSDistribution.distributionError) => new SqDistributionError(e)
);
}
stdev(env: environment) {
return resultMap2(
RSDistribution.stdev({ env }, this._value),
(v: number) => v,
(e: RSDistribution.distributionError) => new SqDistributionError(e)
);
}
}
export class SqPointSetDistribution extends SqAbstractDistribution {
tag = Tag.PointSet;
value() {
return this.valueMethod(RSDistribution.getPointSet);
}
}
export class SqSampleSetDistribution extends SqAbstractDistribution {
tag = Tag.SampleSet;
value() {
return this.valueMethod(RSDistribution.getSampleSet);
}
}
export class SqSymbolicDistribution extends SqAbstractDistribution {
tag = Tag.Symbolic;
value() {
return this.valueMethod(RSDistribution.getSymbolic);
}
}
const tagToClass = {
[Tag.PointSet]: SqPointSetDistribution,
[Tag.SampleSet]: SqSampleSetDistribution,
[Tag.Symbolic]: SqSymbolicDistribution,
} as const;
export type SqDistribution =
| SqPointSetDistribution
| SqSampleSetDistribution
| SqSymbolicDistribution;

View File

@ -0,0 +1,11 @@
import * as RSDistributionError from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Error.gen";
type T = RSDistributionError.distributionError;
export class SqDistributionError {
constructor(private _value: T) {}
toString() {
return RSDistributionError.toString(this._value);
}
}

View File

@ -0,0 +1,17 @@
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
export class SqError {
constructor(private _value: RSErrorValue.reducerErrorValue) {}
toString() {
return RSErrorValue.toString(this._value);
}
static createTodoError(v: string) {
return new SqError(RSErrorValue.createTodoError(v));
}
static createOtherError(v: string) {
return new SqError(RSErrorValue.createOtherError(v));
}
}

View File

@ -0,0 +1,43 @@
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
import { SqError } from "./SqError";
import { SqValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
import { result } from "./types";
type T = RSLambda.squiggleValue_Lambda;
export class SqLambda {
constructor(private _value: T, public location: SqValueLocation) {}
parameters() {
return RSLambda.parameters(this._value);
}
call(args: (number | string)[]): result<SqValue, SqError> {
const { project, sourceId } = this.location;
// Might be good to use uuid instead, but there's no way to remove sources from projects.
// So this is not thread-safe.
const callId = "__lambda__";
if (this.location.path.root !== "bindings") {
return {
tag: "Error",
value: SqError.createTodoError("Only bindings lambdas can be rendered"),
};
}
const quote = (arg: string) =>
`"${arg.replace(new RegExp('"', "g"), '\\"')}"`;
const argsSource = args
.map((arg) => (typeof arg === "number" ? arg : quote(arg)))
.join(",");
const functionNameSource = this.location.path.items
.map((item, i) =>
typeof item === "string" ? (i ? "." + item : item) : `[${item}]`
)
.join("");
const source = `${functionNameSource}(${argsSource})`;
project.setSource(callId, source);
project.setContinues(callId, [sourceId]);
project.run(callId);
return project.getResult(callId);
}
}

View File

@ -0,0 +1,7 @@
import * as RSDeclaration from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.gen";
type T = RSDeclaration.squiggleValue_Declaration;
export class SqLambdaDeclaration {
constructor(private _value: T) {}
}

View File

@ -0,0 +1,30 @@
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
import { SqModuleValue, wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
export class SqModule {
constructor(
private _value: RSModuleValue.squiggleValue_Module,
public location: SqValueLocation
) {}
entries() {
return RSModuleValue.getKeyValuePairs(this._value).map(
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
);
}
asValue() {
return new SqModuleValue(
RSModuleValue.toSquiggleValue(this._value),
this.location
);
}
get(k: string) {
const v = RSModuleValue.get(this._value, k);
return v === undefined || v === null
? undefined
: wrapValue(v, this.location.extend(k));
}
}

View File

@ -0,0 +1,101 @@
import * as _ from "lodash";
import { wrapDistribution } from "./SqDistribution";
import * as RSPointSetDist from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution.gen";
import { pointSetDistributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution_tag";
type T = RSPointSetDist.pointSetDistribution;
export type SqPoint = { x: number; y: number };
export type SqShape = {
continuous: SqPoint[];
discrete: SqPoint[];
};
const shapePoints = (
x: RSPointSetDist.continuousShape | RSPointSetDist.discreteShape
): SqPoint[] => {
let xs = x.xyShape.xs;
let ys = x.xyShape.ys;
return _.zipWith(xs, ys, (x, y) => ({ x, y }));
};
export const wrapPointSetDist = (value: T) => {
const tag = RSPointSetDist.getTag(value);
return new tagToClass[tag](value);
};
abstract class SqAbstractPointSetDist {
constructor(private _value: T) {}
abstract asShape(): SqShape;
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
const value = rsMethod(this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
asDistribution() {
return wrapDistribution(RSPointSetDist.toDistribution(this._value));
}
}
export class SqMixedPointSetDist extends SqAbstractPointSetDist {
tag = Tag.Mixed as const;
get value(): RSPointSetDist.mixedShape {
return this.valueMethod(RSPointSetDist.getMixed);
}
asShape() {
const v = this.value;
return {
discrete: shapePoints(v.discrete),
continuous: shapePoints(v.continuous),
};
}
}
export class SqDiscretePointSetDist extends SqAbstractPointSetDist {
tag = Tag.Discrete as const;
get value(): RSPointSetDist.discreteShape {
return this.valueMethod(RSPointSetDist.getDiscrete);
}
asShape() {
const v = this.value;
return {
discrete: shapePoints(v),
continuous: [],
};
}
}
export class SqContinuousPointSetDist extends SqAbstractPointSetDist {
tag = Tag.Continuous as const;
get value(): RSPointSetDist.continuousShape {
return this.valueMethod(RSPointSetDist.getContinues);
}
asShape() {
const v = this.value;
return {
discrete: [],
continuous: shapePoints(v),
};
}
}
const tagToClass = {
[Tag.Mixed]: SqMixedPointSetDist,
[Tag.Discrete]: SqDiscretePointSetDist,
[Tag.Continuous]: SqContinuousPointSetDist,
} as const;
export type SqPointSetDist =
| SqMixedPointSetDist
| SqDiscretePointSetDist
| SqContinuousPointSetDist;

View File

@ -0,0 +1,122 @@
import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
import { SqError } from "./SqError";
import { SqModule } from "./SqModule";
import { wrapValue } from "./SqValue";
import { resultMap2 } from "./types";
import { SqValueLocation } from "./SqValueLocation";
export class SqProject {
constructor(private _value: RSProject.reducerProject) {}
static create() {
return new SqProject(RSProject.createProject());
}
getSourceIds() {
return RSProject.getSourceIds(this._value);
}
setSource(sourceId: string, value: string) {
return RSProject.setSource(this._value, sourceId, value);
}
getSource(sourceId: string) {
return RSProject.getSource(this._value, sourceId);
}
touchSource(sourceId: string) {
return RSProject.touchSource(this._value, sourceId);
}
clean(sourceId: string) {
return RSProject.clean(this._value, sourceId);
}
cleanAll() {
return RSProject.cleanAll(this._value);
}
cleanResults(sourceId: string) {
return RSProject.cleanResults(this._value, sourceId);
}
cleanAllResults() {
return RSProject.cleanAllResults(this._value);
}
getIncludes(sourceId: string) {
return resultMap2(
RSProject.getIncludes(this._value, sourceId),
(a) => a,
(v: reducerErrorValue) => new SqError(v)
);
}
getContinues(sourceId: string) {
return RSProject.getContinues(this._value, sourceId);
}
setContinues(sourceId: string, continues: string[]) {
return RSProject.setContinues(this._value, sourceId, continues);
}
getDependencies(sourceId: string) {
return RSProject.getDependencies(this._value, sourceId);
}
getDependents(sourceId: string) {
return RSProject.getDependents(this._value, sourceId);
}
getRunOrder() {
return RSProject.getRunOrder(this._value);
}
getRunOrderFor(sourceId: string) {
return RSProject.getRunOrderFor(this._value, sourceId);
}
parseIncludes(sourceId: string) {
return RSProject.parseIncludes(this._value, sourceId);
}
run(sourceId: string) {
return RSProject.run(this._value, sourceId);
}
runAll() {
return RSProject.runAll(this._value);
}
getBindings(sourceId: string) {
return new SqModule(
RSProject.getBindings(this._value, sourceId),
new SqValueLocation(this, sourceId, {
root: "bindings",
items: [],
})
);
}
getResult(sourceId: string) {
const innerResult = RSProject.getResult(this._value, sourceId);
return resultMap2(
innerResult,
(v) =>
wrapValue(
v,
new SqValueLocation(this, sourceId, {
root: "result",
items: [],
})
),
(v: reducerErrorValue) => new SqError(v)
);
}
setEnvironment(environment: environment) {
RSProject.setEnvironment(this._value, environment);
}
}

View File

@ -0,0 +1,15 @@
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
import { wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
type T = RSRecord.squiggleValue_Record;
export class SqRecord {
constructor(private _value: T, public location: SqValueLocation) {}
entries() {
return RSRecord.getKeyValuePairs(this._value).map(
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
);
}
}

View File

@ -0,0 +1,7 @@
import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.gen";
type T = RSType.squiggleValue_Type;
export class SqType {
constructor(private _value: T) {}
}

View File

@ -0,0 +1,216 @@
import * as RSValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.gen";
import { squiggleValueTag as Tag } from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag";
import { wrapDistribution } from "./SqDistribution";
import { SqLambda } from "./SqLambda";
import { SqLambdaDeclaration } from "./SqLambdaDeclaration";
import { SqModule } from "./SqModule";
import { SqRecord } from "./SqRecord";
import { SqArray } from "./SqArray";
import { SqType } from "./SqType";
import { SqProject } from "./SqProject";
import { SqValueLocation } from "./SqValueLocation";
export { Tag as SqValueTag };
type T = RSValue.squiggleValue;
export const wrapValue = (value: T, location: SqValueLocation): SqValue => {
const tag = RSValue.getTag(value);
return new tagToClass[tag](value, location);
};
export abstract class SqAbstractValue {
abstract tag: Tag;
constructor(private _value: T, public location: SqValueLocation) {}
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
const value = rsMethod(this._value);
if (value === undefined || value === null) {
throw new Error("Internal casting error");
}
return value;
};
toString() {
return RSValue.toString(this._value);
}
}
export class SqArrayValue extends SqAbstractValue {
tag = Tag.Array as const;
get value() {
return new SqArray(this.valueMethod(RSValue.getArray), this.location);
}
}
export class SqArrayStringValue extends SqAbstractValue {
tag = Tag.ArrayString as const;
get value() {
return this.valueMethod(RSValue.getArrayString);
}
}
export class SqBoolValue extends SqAbstractValue {
tag = Tag.Bool as const;
get value() {
return this.valueMethod(RSValue.getBool);
}
}
export class SqCallValue extends SqAbstractValue {
tag = Tag.Call as const;
get value() {
return this.valueMethod(RSValue.getCall);
}
}
export class SqDateValue extends SqAbstractValue {
tag = Tag.Date as const;
get value() {
return this.valueMethod(RSValue.getDate);
}
}
export class SqDeclarationValue extends SqAbstractValue {
tag = Tag.Declaration as const;
get value() {
return new SqLambdaDeclaration(this.valueMethod(RSValue.getDeclaration));
}
}
export class SqDistributionValue extends SqAbstractValue {
tag = Tag.Distribution as const;
get value() {
return wrapDistribution(this.valueMethod(RSValue.getDistribution));
}
}
export class SqLambdaValue extends SqAbstractValue {
tag = Tag.Lambda as const;
get value() {
return new SqLambda(this.valueMethod(RSValue.getLambda), this.location);
}
}
export class SqModuleValue extends SqAbstractValue {
tag = Tag.Module as const;
get value() {
return new SqModule(this.valueMethod(RSValue.getModule), this.location);
}
}
export class SqNumberValue extends SqAbstractValue {
tag = Tag.Number as const;
get value() {
return this.valueMethod(RSValue.getNumber);
}
}
export class SqRecordValue extends SqAbstractValue {
tag = Tag.Record as const;
get value() {
return new SqRecord(this.valueMethod(RSValue.getRecord), this.location);
}
}
export class SqStringValue extends SqAbstractValue {
tag = Tag.String as const;
get value(): string {
return this.valueMethod(RSValue.getString);
}
}
export class SqSymbolValue extends SqAbstractValue {
tag = Tag.Symbol as const;
get value(): string {
return this.valueMethod(RSValue.getSymbol);
}
}
export class SqTimeDurationValue extends SqAbstractValue {
tag = Tag.TimeDuration as const;
get value() {
return this.valueMethod(RSValue.getTimeDuration);
}
}
export class SqTypeValue extends SqAbstractValue {
tag = Tag.Type as const;
get value() {
return new SqType(this.valueMethod(RSValue.getType));
}
}
export class SqTypeIdentifierValue extends SqAbstractValue {
tag = Tag.TypeIdentifier as const;
get value() {
return this.valueMethod(RSValue.getTypeIdentifier);
}
}
export class SqVoidValue extends SqAbstractValue {
tag = Tag.Void as const;
get value() {
return null;
}
}
const tagToClass = {
[Tag.Array]: SqArrayValue,
[Tag.ArrayString]: SqArrayStringValue,
[Tag.Bool]: SqBoolValue,
[Tag.Call]: SqCallValue,
[Tag.Date]: SqDateValue,
[Tag.Declaration]: SqDeclarationValue,
[Tag.Distribution]: SqDistributionValue,
[Tag.Lambda]: SqLambdaValue,
[Tag.Module]: SqModuleValue,
[Tag.Number]: SqNumberValue,
[Tag.Record]: SqRecordValue,
[Tag.String]: SqStringValue,
[Tag.Symbol]: SqSymbolValue,
[Tag.TimeDuration]: SqTimeDurationValue,
[Tag.Type]: SqTypeValue,
[Tag.TypeIdentifier]: SqTypeIdentifierValue,
[Tag.Void]: SqVoidValue,
} as const;
// FIXME
// type SqValue = typeof tagToClass[keyof typeof tagToClass];
export type SqValue =
| SqArrayValue
| SqArrayStringValue
| SqBoolValue
| SqCallValue
| SqDateValue
| SqDeclarationValue
| SqDistributionValue
| SqLambdaValue
| SqModuleValue
| SqNumberValue
| SqRecordValue
| SqStringValue
| SqSymbolValue
| SqTimeDurationValue
| SqTypeValue
| SqTypeIdentifierValue
| SqVoidValue;

View File

@ -0,0 +1,24 @@
import { isParenthesisNode } from "mathjs";
import { SqProject } from "./SqProject";
type PathItem = string | number;
type SqValuePath = {
root: "result" | "bindings";
items: PathItem[];
};
export class SqValueLocation {
constructor(
public project: SqProject,
public sourceId: string,
public path: SqValuePath
) {}
extend(item: PathItem) {
return new SqValueLocation(this.project, this.sourceId, {
root: this.path.root,
items: [...this.path.items, item],
});
}
}

View File

@ -1,252 +0,0 @@
import * as _ from "lodash";
import {
genericDist,
continuousShape,
discreteShape,
environment,
distributionError,
toPointSet,
distributionErrorToString,
} from "../rescript/TypescriptInterface.gen";
import { result, resultMap, Ok } from "./types";
import {
Constructors_mean,
Constructors_stdev,
Constructors_sample,
Constructors_pdf,
Constructors_cdf,
Constructors_inv,
Constructors_normalize,
Constructors_isNormalized,
Constructors_toPointSet,
Constructors_toSampleSet,
Constructors_truncate,
Constructors_inspect,
Constructors_toString,
Constructors_toSparkline,
Constructors_algebraicAdd,
Constructors_algebraicMultiply,
Constructors_algebraicDivide,
Constructors_algebraicSubtract,
Constructors_algebraicLogarithm,
Constructors_algebraicPower,
Constructors_pointwiseAdd,
Constructors_pointwiseMultiply,
Constructors_pointwiseDivide,
Constructors_pointwiseSubtract,
Constructors_pointwiseLogarithm,
Constructors_pointwisePower,
} from "../rescript/Distributions/DistributionOperation.gen";
export type point = { x: number; y: number };
function shapePoints(x: continuousShape | discreteShape): point[] {
let xs = x.xyShape.xs;
let ys = x.xyShape.ys;
return _.zipWith(xs, ys, (x, y) => ({ x, y }));
}
export type shape = {
continuous: point[];
discrete: point[];
};
export class Distribution {
t: genericDist;
env: environment;
constructor(t: genericDist, env: environment) {
this.t = t;
this.env = env;
return this;
}
mapResultDist(
r: result<genericDist, distributionError>
): result<Distribution, distributionError> {
return resultMap(r, (v: genericDist) => new Distribution(v, this.env));
}
mean(): result<number, distributionError> {
return Constructors_mean({ env: this.env }, this.t);
}
stdev(): result<number, distributionError> {
return Constructors_stdev({ env: this.env }, this.t);
}
sample(): result<number, distributionError> {
return Constructors_sample({ env: this.env }, this.t);
}
pdf(n: number): result<number, distributionError> {
return Constructors_pdf({ env: this.env }, this.t, n);
}
cdf(n: number): result<number, distributionError> {
return Constructors_cdf({ env: this.env }, this.t, n);
}
inv(n: number): result<number, distributionError> {
return Constructors_inv({ env: this.env }, this.t, n);
}
isNormalized(): result<boolean, distributionError> {
return Constructors_isNormalized({ env: this.env }, this.t);
}
normalize(): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_normalize({ env: this.env }, this.t)
);
}
type() {
return this.t.tag;
}
pointSet(): result<shape, distributionError> {
let pointSet = toPointSet(
this.t,
{
xyPointLength: this.env.xyPointLength,
sampleCount: this.env.sampleCount,
},
undefined
);
if (pointSet.tag === "Ok") {
let distribution = pointSet.value;
if (distribution.tag === "Continuous") {
return Ok({
continuous: shapePoints(distribution.value),
discrete: [],
});
} else if (distribution.tag === "Discrete") {
return Ok({
discrete: shapePoints(distribution.value),
continuous: [],
});
} else {
return Ok({
discrete: shapePoints(distribution.value.discrete),
continuous: shapePoints(distribution.value.continuous),
});
}
} else {
return pointSet;
}
}
toPointSet(): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_toPointSet({ env: this.env }, this.t)
);
}
toSampleSet(n: number): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_toSampleSet({ env: this.env }, this.t, n)
);
}
truncate(
left: number,
right: number
): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_truncate({ env: this.env }, this.t, left, right)
);
}
inspect(): result<Distribution, distributionError> {
return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
}
toString(): string {
let result = Constructors_toString({ env: this.env }, this.t);
if (result.tag === "Ok") {
return result.value;
} else {
return distributionErrorToString(result.value);
}
}
toSparkline(n: number): result<string, distributionError> {
return Constructors_toSparkline({ env: this.env }, this.t, n);
}
algebraicAdd(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
);
}
algebraicMultiply(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
);
}
algebraicDivide(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
);
}
algebraicSubtract(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
);
}
algebraicLogarithm(
d2: Distribution
): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
);
}
algebraicPower(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
);
}
pointwiseAdd(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
);
}
pointwiseMultiply(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
);
}
pointwiseDivide(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
);
}
pointwiseSubtract(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
);
}
pointwiseLogarithm(
d2: Distribution
): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
);
}
pointwisePower(d2: Distribution): result<Distribution, distributionError> {
return this.mapResultDist(
Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
);
}
}

View File

@ -1,219 +1,36 @@
/* Some of the types have moved to ForTS__Types. import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
Needs to be reimported here if necessary and distribution related import { SqProject } from "./SqProject";
import { SqValue, SqValueTag } from "./SqValue";
We only need distribution related extras for back compatibility. Umur export { SqValueLocation } from "./SqValueLocation";
export { result } from "../rescript/ForTS/ForTS_Result_tag";
Instead of a global function namespace we should use modules under ForTS directly maybe renaming them for ease. export { SqDistribution, SqDistributionTag } from "./SqDistribution";
export { SqDistributionError } from "./SqDistributionError";
.e.g. Project.run(project) export { SqRecord } from "./SqRecord";
.e.g. Distribution.makeSampleSetDist export { SqLambda } from "./SqLambda";
export { SqProject };
*/ export { SqValue, SqValueTag };
import * as _ from "lodash";
import type {
environment,
expressionValue,
externalBindings,
errorValue,
} from "../rescript/TypescriptInterface.gen";
import {
defaultEnvironment,
evaluatePartialUsingExternalBindings,
evaluateUsingOptions,
foreignFunctionInterface,
} from "../rescript/TypescriptInterface.gen";
export { export {
makeSampleSetDist, environment,
errorValueToString, defaultEnvironment,
distributionErrorToString, } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
} from "../rescript/TypescriptInterface.gen"; export { SqError } from "./SqError";
export type { export { SqShape } from "./SqPointSetDist";
distributionError,
declarationArg,
declaration,
} from "../rescript/TypescriptInterface.gen";
export type { errorValue, externalBindings as bindings, jsImports };
import {
jsValueToBinding,
jsValueToExpressionValue,
jsValue,
rescriptExport,
squiggleExpression,
convertRawToTypescript,
lambdaValue,
} from "./rescript_interop";
import { result, resultMap, tag, tagged } from "./types";
import { Distribution, shape } from "./distribution";
export { Distribution, resultMap, defaultEnvironment }; export { resultMap } from "./types";
export type { result, shape, environment, lambdaValue, squiggleExpression };
export { parse } from "./parse"; export const run = (
code: string,
export let defaultSamplingInputs: environment = { options?: {
sampleCount: 10000, environment?: environment;
xyPointLength: 10000, }
) => {
const project = SqProject.create();
project.setSource("main", code);
if (options?.environment) {
project.setEnvironment(options.environment);
}
project.run("main");
const result = project.getResult("main");
const bindings = project.getBindings("main");
return { result, bindings };
}; };
/* Umur: All the functions below are invalid. ForTS_Reducer project is the new way to do this. */
// export function run(
// squiggleString: string,
// bindings?: externalBindings,
// environment?: environment,
// imports?: jsImports
// ): result<squiggleExpression, errorValue> {
// let b = bindings ? bindings : defaultBindings;
// let i = imports ? imports : defaultImports;
// let e = environment ? environment : defaultEnvironment;
// let res: result<expressionValue, errorValue> = evaluateUsingOptions(
// { externalBindings: mergeImportsWithBindings(b, i), environment: e },
// squiggleString
// );
// return resultMap(res, (x) => createTsExport(x, e));
// }
// // Run Partial. A partial is a block of code that doesn't return a value
// export function runPartial(
// squiggleString: string,
// bindings?: externalBindings,
// environment?: environment,
// imports?: jsImports
// ): result<externalBindings, errorValue> {
// let b = bindings ? bindings : defaultBindings;
// let i = imports ? imports : defaultImports;
// let e = environment ? environment : defaultEnvironment;
// return evaluatePartialUsingExternalBindings(
// squiggleString,
// mergeImportsWithBindings(b, i),
// e
// );
// }
// export function runForeign(
// fn: lambdaValue,
// args: jsValue[],
// environment?: environment
// ): result<squiggleExpression, errorValue> {
// let e = environment ? environment : defaultEnvironment;
// let res: result<expressionValue, errorValue> = foreignFunctionInterface(
// fn,
// args.map(jsValueToExpressionValue),
// e
// );
// return resultMap(res, (x) => createTsExport(x, e));
// }
// function mergeImportsWithBindings(
// bindings: externalBindings,
// imports: jsImports
// ): externalBindings {
// let transformedImports = Object.fromEntries(
// Object.entries(imports).map(([key, value]) => [
// "$" + key,
// jsValueToBinding(value),
// ])
// );
// return _.merge(bindings, transformedImports);
// }
// type jsImports = { [key: string]: jsValue };
// export let defaultImports: jsImports = {};
// export let defaultBindings: externalBindings = {};
// export function mergeBindings(
// allBindings: externalBindings[]
// ): externalBindings {
// return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
// }
// function createTsExport(
// x: expressionValue,
// environment: environment
// ): squiggleExpression {
// switch (x) {
// case "EvVoid":
// return tag("void", "");
// default: {
// switch (x.tag) {
// case "EvArray":
// // genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
// // format, leaving it as the raw values. This converts the raw values
// // directly into typescript values.
// //
// // The casting here is because genType is about the types of the returned
// // values, claiming they are fully recursive when that's not actually the
// // case
// return tag(
// "array",
// x.value.map(
// (arrayItem): squiggleExpression =>
// convertRawToTypescript(
// arrayItem as unknown as rescriptExport,
// environment
// )
// )
// );
// case "EvArrayString":
// return tag("arraystring", x.value);
// case "EvBool":
// return tag("boolean", x.value);
// case "EvCall":
// return tag("call", x.value);
// case "EvLambda":
// return tag("lambda", x.value);
// case "EvDistribution":
// return tag("distribution", new Distribution(x.value, environment));
// case "EvNumber":
// return tag("number", x.value);
// case "EvRecord":
// // genType doesn't support records, so we have to do the raw conversion ourself
// let result: tagged<"record", { [key: string]: squiggleExpression }> =
// tag(
// "record",
// _.mapValues(x.value, (x: unknown) =>
// convertRawToTypescript(x as rescriptExport, environment)
// )
// );
// return result;
// case "EvString":
// return tag("string", x.value);
// case "EvSymbol":
// return tag("symbol", x.value);
// case "EvDate":
// return tag("date", x.value);
// case "EvTimeDuration":
// return tag("timeDuration", x.value);
// case "EvDeclaration":
// return tag("lambdaDeclaration", x.value);
// case "EvTypeIdentifier":
// return tag("typeIdentifier", x.value);
// case "EvType":
// let typeResult: tagged<
// "type",
// { [key: string]: squiggleExpression }
// > = tag(
// "type",
// _.mapValues(x.value, (x: unknown) =>
// convertRawToTypescript(x as rescriptExport, environment)
// )
// );
// return typeResult;
// case "EvModule":
// let moduleResult: tagged<
// "module",
// { [key: string]: squiggleExpression }
// > = tag(
// "module",
// _.mapValues(x.value, (x: unknown) =>
// convertRawToTypescript(x as rescriptExport, environment)
// )
// );
// return moduleResult;
// }
// }
// }
// }

View File

@ -1,23 +1,23 @@
import { // import {
errorValue, // errorValue,
parse as parseRescript, // parse as parseRescript,
} from "../rescript/TypescriptInterface.gen"; // } from "../rescript/TypescriptInterface.gen";
import { result } from "./types"; // import { result } from "./types";
import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers"; // import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers";
export function parse( // export function parse(
squiggleString: string // squiggleString: string
): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> { // ): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> {
const maybeExpression = parseRescript(squiggleString); // const maybeExpression = parseRescript(squiggleString);
if (maybeExpression.tag === "Ok") { // if (maybeExpression.tag === "Ok") {
return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode }; // return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode };
} else { // } else {
if ( // if (
typeof maybeExpression.value !== "object" || // typeof maybeExpression.value !== "object" ||
maybeExpression.value.tag !== "RESyntaxError" // maybeExpression.value.tag !== "RESyntaxError"
) { // ) {
throw new Error("Expected syntax error"); // throw new Error("Expected syntax error");
} // }
return { tag: "Error", value: maybeExpression.value }; // return { tag: "Error", value: maybeExpression.value };
} // }
} // }

View File

@ -1,279 +0,0 @@
/**
Umur: Delete this file! There is nothing left to see here.
**/
// import * as _ from "lodash";
// import type {
// // expressionValue,
// mixedShape,
// sampleSetDist,
// genericDist,
// // environment,
// symbolicDist,
// discreteShape,
// continuousShape,
// // lambdaValue,
// // lambdaDeclaration,
// // declarationArg,
// } from "../rescript/TypescriptInterface.gen";
// import { Distribution } from "./distribution";
// import { tagged, tag } from "./types";
// This file is here to compensate for genType not fully recursively converting types
// Raw rescript types.
// Umur: Rescript expression values are opaque!
// export type rescriptExport =
// | 0 // EvVoid
// | {
// TAG: 0; // EvArray
// _0: rescriptExport[];
// }
// | {
// TAG: 1; // EvString
// _0: string[];
// }
// | {
// TAG: 2; // EvBool
// _0: boolean;
// }
// | {
// TAG: 3; // EvCall
// _0: string;
// }
// | {
// TAG: 4; // EvDistribution
// _0: rescriptDist;
// }
// | {
// TAG: 5; // EvLambda
// _0: lambdaValue;
// }
// | {
// TAG: 6; // EvNumber
// _0: number;
// }
// | {
// TAG: 7; // EvRecord
// _0: { [key: string]: rescriptExport };
// }
// | {
// TAG: 8; // EvString
// _0: string;
// }
// | {
// TAG: 9; // EvSymbol
// _0: string;
// }
// | {
// TAG: 10; // EvDate
// _0: Date;
// }
// | {
// TAG: 11; // EvTimeDuration
// _0: number;
// }
// | {
// TAG: 12; // EvDeclaration
// _0: rescriptLambdaDeclaration;
// }
// | {
// TAG: 13; // EvTypeIdentifier
// _0: string;
// }
// | {
// TAG: 14; // EvModule
// _0: { [key: string]: rescriptExport };
// };
// Umur: opaque type
// type rescriptDist =
// | { TAG: 0; _0: rescriptPointSetDist }
// | { TAG: 1; _0: sampleSetDist }
// | { TAG: 2; _0: symbolicDist };
// Umur: opaque type, no conversion
// type rescriptPointSetDist =
// | {
// TAG: 0; // Mixed
// _0: mixedShape;
// }
// | {
// TAG: 1; // Discrete
// _0: discreteShape;
// }
// | {
// TAG: 2; // ContinuousShape
// _0: continuousShape;
// };
// type rescriptLambdaDeclaration = {
// readonly fn: lambdaValue;
// readonly args: rescriptDeclarationArg[];
// };
// type rescriptDeclarationArg =
// | {
// TAG: 0; // Float
// min: number;
// max: number;
// }
// | {
// TAG: 1; // Date
// min: Date;
// max: Date;
// };
// Umur: Squiggle expressions are opaque!
// export type squiggleExpression =
// | tagged<"symbol", string>
// | tagged<"string", string>
// | tagged<"call", string>
// | tagged<"lambda", lambdaValue>
// | tagged<"array", squiggleExpression[]>
// | tagged<"arraystring", string[]>
// | tagged<"boolean", boolean>
// | tagged<"distribution", Distribution>
// | tagged<"number", number>
// | tagged<"date", Date>
// | tagged<"timeDuration", number>
// | tagged<"lambdaDeclaration", lambdaDeclaration>
// | tagged<"record", { [key: string]: squiggleExpression }>
// | tagged<"type", { [key: string]: squiggleExpression }>
// | tagged<"typeIdentifier", string>
// | tagged<"module", { [key: string]: squiggleExpression }>
// | tagged<"void", string>;
// export { lambdaValue };
// Umur: Opaque type no conversion!
// export function convertRawToTypescript(
// result: rescriptExport,
// environment: environment
// ): squiggleExpression {
// if (typeof result === "number") {
// // EvVoid
// return tag("void", "");
// }
// switch (result.TAG) {
// case 0: // EvArray
// return tag(
// "array",
// result._0.map((x) => convertRawToTypescript(x, environment))
// );
// case 1: // EvArrayString
// return tag("arraystring", result._0);
// case 2: // EvBool
// return tag("boolean", result._0);
// case 3: // EvCall
// return tag("call", result._0);
// case 4: // EvDistribution
// return tag(
// "distribution",
// new Distribution(
// convertRawDistributionToGenericDist(result._0),
// environment
// )
// );
// case 5: // EvDistribution
// return tag("lambda", result._0);
// case 6: // EvNumber
// return tag("number", result._0);
// case 7: // EvRecord
// return tag(
// "record",
// _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
// );
// case 8: // EvString
// return tag("string", result._0);
// case 9: // EvSymbol
// return tag("symbol", result._0);
// case 10: // EvDate
// return tag("date", result._0);
// case 11: // EvTimeDuration
// return tag("number", result._0);
// case 12: // EvDeclaration
// return tag("lambdaDeclaration", {
// fn: result._0.fn,
// args: result._0.args.map(convertDeclaration),
// });
// case 13: // EvSymbol
// return tag("typeIdentifier", result._0);
// case 14: // EvModule
// return tag(
// "module",
// _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
// );
// }
// }
// function convertDeclaration(
// declarationArg: rescriptDeclarationArg
// ): declarationArg {
// switch (declarationArg.TAG) {
// case 0: // Float
// return tag("Float", { min: declarationArg.min, max: declarationArg.max });
// case 1: // Date
// return tag("Date", { min: declarationArg.min, max: declarationArg.max });
// }
// }
// Umur: opaque type no conversion!
// function convertRawDistributionToGenericDist(
// result: rescriptDist
// ): genericDist {
// switch (result.TAG) {
// case 0: // Point Set Dist
// switch (result._0.TAG) {
// case 0: // Mixed
// return tag("PointSet", tag("Mixed", result._0._0));
// case 1: // Discrete
// return tag("PointSet", tag("Discrete", result._0._0));
// case 2: // Continuous
// return tag("PointSet", tag("Continuous", result._0._0));
// }
// case 1: // Sample Set Dist
// return tag("SampleSet", result._0);
// case 2: // Symbolic Dist
// return tag("Symbolic", result._0);
// }
// }
// export type jsValue =
// | string
// | number
// | jsValue[]
// | { [key: string]: jsValue }
// | boolean;
// export function jsValueToBinding(value: jsValue): rescriptExport {
// if (typeof value === "boolean") {
// return { TAG: 2, _0: value as boolean };
// } else if (typeof value === "string") {
// return { TAG: 8, _0: value as string };
// } else if (typeof value === "number") {
// return { TAG: 6, _0: value as number };
// } else if (Array.isArray(value)) {
// return { TAG: 0, _0: value.map(jsValueToBinding) };
// } else {
// // Record
// return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
// }
// }
// export function jsValueToExpressionValue(value: jsValue): expressionValue {
// if (typeof value === "boolean") {
// return { tag: "EvBool", value: value as boolean };
// } else if (typeof value === "string") {
// return { tag: "EvString", value: value as string };
// } else if (typeof value === "number") {
// return { tag: "EvNumber", value: value as number };
// } else if (Array.isArray(value)) {
// return { tag: "EvArray", value: value.map(jsValueToExpressionValue) };
// } else {
// // Record
// return {
// tag: "EvRecord",
// value: _.mapValues(value, jsValueToExpressionValue),
// };
// }
// }

View File

@ -1,30 +1,29 @@
export type result<a, b> = import { result } from "../rescript/ForTS/ForTS_Result_tag";
| { export { result };
tag: "Ok";
value: a;
}
| {
tag: "Error";
value: b;
};
export function resultMap<a, b, c>( export function resultMap<a, b, c>(
r: result<a, c>, r: result<a, b>,
mapFn: (x: a) => b mapValue: (x: a) => c
): result<b, c> { ): result<c, b> {
if (r.tag === "Ok") { if (r.tag === "Ok") {
return { tag: "Ok", value: mapFn(r.value) }; return { tag: "Ok", value: mapValue(r.value) };
} else { } else {
return r; return { tag: "Error", value: r.value };
}
}
export function resultMap2<a, b, c, d>(
r: result<a, b>,
mapValue: (x: a) => c,
mapError: (y: b) => d
): result<c, d> {
if (r.tag === "Ok") {
return { tag: "Ok", value: mapValue(r.value) };
} else {
return { tag: "Error", value: mapError(r.value) };
} }
} }
export function Ok<a, b>(x: a): result<a, b> { export function Ok<a, b>(x: a): result<a, b> {
return { tag: "Ok", value: x }; return { tag: "Ok", value: x };
} }
export type tagged<a, b> = { tag: a; value: b };
export function tag<a, b>(x: a, y: b): tagged<a, b> {
return { tag: x, value: y };
}

View File

@ -1,5 +1,6 @@
// Genetic Distribution happens to be abstract distribution // Genetic Distribution happens to be abstract distribution
@genType type distribution = DistributionTypes.genericDist @genType type distribution = DistributionTypes.genericDist
@genType type distributionError = DistributionTypes.error
@genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution @genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution
@genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution @genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution
@genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution @genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution
@ -9,19 +10,19 @@ type environment = ForTS_Distribution_Environment.environment //use
@genType @genType
let defaultEnvironment: environment = DistributionOperation.defaultEnv let defaultEnvironment: environment = DistributionOperation.defaultEnv
@module("ForTS_Distribution_tag") @scope("distributionTag") @module("./ForTS_Distribution_tag") @scope("distributionTag")
external dtPointSet_: int = "DtPointSet" external dtPointSet_: string = "PointSet"
@module("ForTS_Distribution_tag") @scope("distributionTag") @module("./ForTS_Distribution_tag") @scope("distributionTag")
external dtSampleSet_: int = "DtSampleSet" external dtSampleSet_: string = "SampleSet"
@module("ForTS_Distribution_tag") @scope("distributionTag") @module("./ForTS_Distribution_tag") @scope("distributionTag")
external dtSymbolic_: int = "DtSymbolic" external dtSymbolic_: string = "Symbolic"
@genType.import("./ForTS_Distribution_tag") @genType.import("./ForTS_Distribution_tag")
type distributionTag type distributionTag
external castEnum: int => distributionTag = "%identity" external castEnum: string => distributionTag = "%identity"
// type genericDist = // type genericDist =
// | PointSet(PointSetTypes.pointSetDist) // | PointSet(PointSetTypes.pointSetDist)
@ -73,3 +74,15 @@ let inv = DistributionOperation.Constructors.inv
let pdf = DistributionOperation.Constructors.pdf let pdf = DistributionOperation.Constructors.pdf
@genType @genType
let normalize = DistributionOperation.Constructors.normalize let normalize = DistributionOperation.Constructors.normalize
@genType
let toPointSet = (variant: distribution, env: environment) =>
GenericDist.toPointSet(
variant,
~sampleCount=env.sampleCount,
~xyPointLength=env.xyPointLength,
(),
)
@genType
let toString = (variant: distribution) => GenericDist.toString(variant)

View File

@ -1,18 +1,21 @@
@genType type pointSetDistribution = PointSetTypes.pointSetDist @genType type pointSetDistribution = PointSetTypes.pointSetDist
@genType type continuousShape = PointSetTypes.continuousShape
@genType type discreteShape = PointSetTypes.discreteShape
@genType type mixedShape = PointSetTypes.mixedShape
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag") @module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstMixed_: int = "PstMixed" external pstMixed_: string = "Mixed"
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag") @module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstDiscrete_: int = "PstDiscrete" external pstDiscrete_: string = "Discrete"
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag") @module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstContinuous_: int = "PstContinuous" external pstContinuous_: string = "Continuous"
@genType.import("./ForTS_Distribution_PointSetDistribution_tag") @genType.import("./ForTS_Distribution_PointSetDistribution_tag")
type pointSetDistributionTag type pointSetDistributionTag
external castEnum: int => pointSetDistributionTag = "%identity" external castEnum: string => pointSetDistributionTag = "%identity"
@genType @genType
let getTag = (variant: pointSetDistribution): pointSetDistributionTag => let getTag = (variant: pointSetDistribution): pointSetDistributionTag =>
@ -42,3 +45,6 @@ let getContinues = (variant: pointSetDistribution): 'd =>
| Continuous(continuous) => continuous->Some | Continuous(continuous) => continuous->Some
| _ => None | _ => None
} }
@genType
let toDistribution = (v: pointSetDistribution): DistributionTypes.genericDist => PointSet(v)

View File

@ -1,15 +0,0 @@
"use strict";
exports.__esModule = true;
exports.pointSetDistributionTag = void 0;
var pointSetDistributionTag;
(function (pointSetDistributionTag) {
pointSetDistributionTag[(pointSetDistributionTag["PstMixed"] = 0)] =
"PstMixed";
pointSetDistributionTag[(pointSetDistributionTag["PstDiscrete"] = 1)] =
"PstDiscrete";
pointSetDistributionTag[(pointSetDistributionTag["PstContinuous"] = 2)] =
"PstContinuous";
})(
(pointSetDistributionTag =
exports.pointSetDistributionTag || (exports.pointSetDistributionTag = {}))
);

View File

@ -1,5 +1,5 @@
export enum pointSetDistributionTag { export enum pointSetDistributionTag {
PstMixed, Mixed = "Mixed",
PstDiscrete, Discrete = "Discrete",
PstContinuous, Continuous = "Continuous",
} }

View File

@ -1,5 +1,5 @@
export enum distributionTag { export enum distributionTag {
DtPointSet, PointSet = "PointSet",
DtSampleSet, SampleSet = "SampleSet",
DtSymbolic, Symbolic = "Symbolic",
} }

View File

@ -10,3 +10,9 @@ let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> =>
| RESyntaxError(_, optionalLocation) => optionalLocation | RESyntaxError(_, optionalLocation) => optionalLocation
| _ => None | _ => None
} }
@genType
let createTodoError = (v: string) => Reducer_ErrorValue.RETodo(v)
@genType
let createOtherError = (v: string) => Reducer_ErrorValue.REOther(v)

View File

@ -12,55 +12,55 @@ type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //us
// Return values are kept as they are if they are JavaScript types. // Return values are kept as they are if they are JavaScript types.
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArray_: string = "SvtArray" external svtArray_: string = "Array"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArrayString_: string = "SvtArrayString" external svtArrayString_: string = "ArrayString"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtBool_: string = "SvtBool" external svtBool_: string = "Bool"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtCall_: string = "SvtCall" external svtCall_: string = "Call"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDate_: string = "SvtDate" external svtDate_: string = "Date"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDeclaration_: string = "SvtDeclaration" external svtDeclaration_: string = "Declaration"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDistribution_: string = "SvtDistribution" external svtDistribution_: string = "Distribution"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtLambda_: string = "SvtLambda" external svtLambda_: string = "Lambda"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtModule_: string = "SvtModule" external svtModule_: string = "Module"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtNumber_: string = "SvtNumber" external svtNumber_: string = "Number"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtRecord_: string = "SvtRecord" external svtRecord_: string = "Record"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtString_: string = "SvtString" external svtString_: string = "String"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtSymbol_: string = "SvtSymbol" external svtSymbol_: string = "Symbol"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTimeDuration_: string = "SvtTimeDuration" external svtTimeDuration_: string = "TimeDuration"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtType_: string = "SvtType" external svtType_: string = "Type"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTypeIdentifier_: string = "SvtUndefined" external svtTypeIdentifier_: string = "TypeIdentifier"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtVoid_: string = "SvtVoid" external svtVoid_: string = "Void"
@genType.import("./ForTS_SquiggleValue_tag") @genType.import("./ForTS_SquiggleValue_tag")
type squiggleValueTag type squiggleValueTag

View File

@ -8,6 +8,3 @@ let toString = (v: squiggleValue_Lambda): string =>
let parameters = (v: squiggleValue_Lambda): array<string> => { let parameters = (v: squiggleValue_Lambda): array<string> => {
v.parameters v.parameters
} }
@genType
let getParameters = parameters

View File

@ -8,3 +8,9 @@ let getKeyValuePairs = (v: squiggleValue_Module): array<(string, squiggleValue)>
@genType @genType
let toString = (v: squiggleValue_Module): string => let toString = (v: squiggleValue_Module): string =>
ReducerInterface_InternalExpressionValue.toStringNameSpace(v) ReducerInterface_InternalExpressionValue.toStringNameSpace(v)
@genType
let toSquiggleValue = (v: squiggleValue_Module): squiggleValue => IEvBindings(v)
@genType
let get = ReducerInterface_InternalExpressionValue.nameSpaceGet

View File

@ -1,19 +1,19 @@
export enum squiggleValueTag { export enum squiggleValueTag {
SvtArray = "Array", Array = "Array",
SvtArrayString = "ArrayString", ArrayString = "ArrayString",
SvtBool = "Bool", Bool = "Bool",
SvtCall = "Call", Call = "Call",
SvtDate = "Date", Date = "Date",
SvtDeclaration = "Declaration", Declaration = "Declaration",
SvtDistribution = "Distribution", Distribution = "Distribution",
SvtLambda = "Lambda", Lambda = "Lambda",
SvtModule = "Module", Module = "Module",
SvtNumber = "Number", Number = "Number",
SvtRecord = "Record", Record = "Record",
SvtString = "String", String = "String",
SvtSymbol = "Symbol", Symbol = "Symbol",
SvtTimeDuration = "TimeDuration", TimeDuration = "TimeDuration",
SvtType = "Type", Type = "Type",
SvtTypeIdentifier = "TypeIdentifier", TypeIdentifier = "TypeIdentifier",
SvtVoid = "Void", Void = "Void",
} }

View File

@ -22,6 +22,7 @@ type errorValue =
| RETodo(string) // To do | RETodo(string) // To do
| REUnitNotFound(string) | REUnitNotFound(string)
| RENeedToRun | RENeedToRun
| REOther(string)
type t = errorValue type t = errorValue
@ -59,4 +60,5 @@ let errorToString = err =>
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}` | REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
| REUnitNotFound(unitName) => `Unit not found: ${unitName}` | REUnitNotFound(unitName) => `Unit not found: ${unitName}`
| RENeedToRun => "Need to run" | RENeedToRun => "Need to run"
| REOther(msg) => `Error: ${msg}`
} }

View File

@ -239,3 +239,9 @@ let nameSpaceToKeyValuePairs = (nameSpace: nameSpace): array<(string, t)> => {
let NameSpace(container) = nameSpace let NameSpace(container) = nameSpace
container->Belt.Map.String.toArray container->Belt.Map.String.toArray
} }
let nameSpaceGet = (nameSpace: nameSpace, key: string): option<t> => {
let NameSpace(container) = nameSpace
container->Belt.Map.String.get(key)
}

View File

@ -2,7 +2,7 @@ module Bindings = Reducer_Bindings
let bindings: Bindings.t = let bindings: Bindings.t =
[ [
("System.version", ReducerInterface_InternalExpressionValue.IEvString("0.3.0")), ("System.version", ReducerInterface_InternalExpressionValue.IEvString("0.4.0-dev")),
]->Bindings.fromArray ]->Bindings.fromArray
let makeBindings = (previousBindings: Bindings.t): Bindings.t => let makeBindings = (previousBindings: Bindings.t): Bindings.t =>

View File

@ -11,9 +11,9 @@
"outDir": "./dist", "outDir": "./dist",
"declarationDir": "./dist", "declarationDir": "./dist",
"declaration": true, "declaration": true,
"composite": true "composite": true,
"target": "ES6"
}, },
"target": "ES6",
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts", "webpack.config.js"] "exclude": ["node_modules", "**/*.spec.ts", "webpack.config.js"]
} }