From fc68a8d0694fa9dfe5077fecf88d0a39220df7e9 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 24 Jun 2022 03:43:30 +0000 Subject: [PATCH 01/29] Remove observable boilerplate + refactors --- packages/components/package.json | 2 - .../src/components/SquiggleChart.tsx | 22 ++- .../src/components/SquiggleEditor.tsx | 150 +++--------------- .../src/components/SquigglePlayground.tsx | 16 +- packages/components/src/index.ts | 12 +- packages/components/src/lib/hooks.ts | 23 +-- .../src/stories/SquiggleEditor.stories.mdx | 4 +- .../src/stories/SquigglePartial.stories.mdx | 6 +- packages/website/docs/Discussions/Bugs.mdx | 4 +- .../docs/Guides/DistributionCreation.mdx | 64 ++++---- packages/website/docs/Guides/Functions.mdx | 58 +++---- packages/website/docs/Guides/Language.mdx | 12 +- yarn.lock | 2 +- 13 files changed, 120 insertions(+), 255 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 7c64463c..bd86a257 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -38,7 +38,6 @@ "@types/lodash": "^4.14.182", "@types/node": "^18.0.0", "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.5", "@types/styled-components": "^5.1.24", "@types/webpack": "^5.28.0", "cross-env": "^7.0.3", @@ -47,7 +46,6 @@ "postcss-import": "^14.1.0", "postcss-loader": "^7.0.0", "react": "^18.1.0", - "react-dom": "^18.2.0", "react-scripts": "^5.0.1", "style-loader": "^3.3.1", "tailwindcss": "^3.1.3", diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index f7bb7ace..3e712396 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -8,7 +8,6 @@ import { defaultBindings, defaultEnvironment, } from "@quri/squiggle-lang"; -import { FunctionChartSettings } from "./FunctionChart"; import { useSquiggle } from "../lib/hooks"; import { SquiggleErrorAlert } from "./SquiggleErrorAlert"; import { SquiggleItem } from "./SquiggleItem"; @@ -20,8 +19,12 @@ export interface SquiggleChartProps { sampleCount?: number; /** The amount of points returned to draw the distribution */ environment?: environment; - /** If the result is a function, where the function starts, ends and the amount of stops */ - chartSettings?: FunctionChartSettings; + /** If the result is a function, where the function domain starts */ + diagramStart?: number; + /** If the result is a function, where the function domain ends */ + diagramStop?: number; + /** If the result is a function, the amount of stops sampled */ + diagramCount?: number; /** When the squiggle code gets reevaluated */ onChange?(expr: squiggleExpression | undefined): void; /** CSS width of the element */ @@ -44,7 +47,6 @@ export interface SquiggleChartProps { } const defaultOnChange = () => {}; -const defaultChartSettings = { start: 0, stop: 10, count: 20 }; export const SquiggleChart: React.FC = ({ squiggleString = "", @@ -59,9 +61,11 @@ export const SquiggleChart: React.FC = ({ showControls = false, logX = false, expY = false, - chartSettings = defaultChartSettings, + diagramStart = 0, + diagramStop = 10, + diagramCount = 100, }) => { - const { result } = useSquiggle({ + const result = useSquiggle({ code: squiggleString, bindings, environment, @@ -80,6 +84,12 @@ export const SquiggleChart: React.FC = ({ expY, }; + let chartSettings = { + start: diagramStart, + stop: diagramStop, + count: diagramCount, + }; + return ( ); -export interface SquiggleEditorProps { - /** The input string for squiggle */ - initialSquiggleString?: string; - /** The width of the element */ - width?: number; - /** If the result is a function, where the function starts */ - diagramStart?: number; - /** If the result is a function, where the function ends */ - diagramStop?: number; - /** If the result is a function, how many points along the function it samples */ - diagramCount?: number; - /** When the environment changes. Used again for notebook magic */ - onChange?(expr: squiggleExpression | undefined): void; - /** Previous variable declarations */ - bindings?: bindings; - /** If the output requires monte carlo sampling, the amount of samples */ - environment?: environment; - /** JS Imports */ - jsImports?: jsImports; - /** Whether to show detail about types of the returns, default false */ - showTypes?: boolean; - /** Whether to give users access to graph controls */ - showControls?: boolean; - /** Whether to show a summary table */ - showSummary?: boolean; - /** Whether to log the x coordinate on distribution charts */ - logX?: boolean; - /** Whether to exp the y coordinate on distribution charts */ - expY?: boolean; -} +export type SquiggleEditorProps = SquiggleChartProps; -export const SquiggleEditor: React.FC = ({ - initialSquiggleString = "", - width, - diagramStart = 0, - diagramStop = 10, - diagramCount = 20, - onChange, - bindings = defaultBindings, - environment, - jsImports = defaultImports, - showTypes = false, - showControls = false, - showSummary = false, - logX = false, - expY = false, -}: SquiggleEditorProps) => { - const [code, setCode] = useState(initialSquiggleString); - React.useEffect( - () => setCode(initialSquiggleString), - [initialSquiggleString] - ); - - const { result, observableRef } = useSquiggle({ - code, - bindings, - environment, - jsImports, - onChange, - }); - - const chartSettings = { - start: diagramStart, - stop: diagramStop, - count: diagramCount, - }; - - const distributionPlotSettings = { - showControls, - showSummary, - logX, - expY, - }; +export const SquiggleEditor: React.FC = (props) => { + const { squiggleString = "" } = props; + const [code, setCode] = useState(squiggleString); + React.useEffect(() => setCode(squiggleString), [squiggleString]); + let chartProps = { ...props, squiggleString: code }; return ( -
- - - {result.tag === "Ok" ? ( - - ) : ( - - )} - -
+ + + + ); }; -export function renderSquiggleEditorToDom(props: SquiggleEditorProps) { - const parent = document.createElement("div"); - ReactDOM.render(, parent); - return parent; -} - export interface SquigglePartialProps { /** The input string for squiggle */ - initialSquiggleString?: string; + squiggleString?: string; /** when the environment changes. Used again for notebook magic*/ onChange?(expr: bindings | undefined): void; /** Previously declared variables */ @@ -145,19 +52,16 @@ export interface SquigglePartialProps { } export const SquigglePartial: React.FC = ({ - initialSquiggleString = "", + squiggleString = "", onChange, bindings = defaultBindings, environment, jsImports = defaultImports, }: SquigglePartialProps) => { - const [code, setCode] = useState(initialSquiggleString); - React.useEffect( - () => setCode(initialSquiggleString), - [initialSquiggleString] - ); + const [code, setCode] = useState(squiggleString); + React.useEffect(() => setCode(squiggleString), [squiggleString]); - const { result, observableRef } = useSquigglePartial({ + const result = useSquigglePartial({ code, bindings, environment, @@ -166,19 +70,9 @@ export const SquigglePartial: React.FC = ({ }); return ( -
- - - {result.tag !== "Ok" ? ( - - ) : null} - -
+ + + {result.tag !== "Ok" ? : null} + ); }; - -export function renderSquigglePartialToDom(props: SquigglePartialProps) { - const parent = document.createElement("div"); - ReactDOM.render(, parent); - return parent; -} diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 2cd755b2..ce59b5a4 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -1,5 +1,4 @@ import React, { FC, Fragment, useState, useEffect } from "react"; -import ReactDOM from "react-dom"; import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; @@ -250,11 +249,6 @@ export const SquigglePlayground: FC = ({ onSettingsChange?.(vars); }, [vars, onSettingsChange]); - const chartSettings = { - start: Number(vars.diagramStart), - stop: Number(vars.diagramStop), - count: Number(vars.diagramCount), - }; const env: environment = { sampleCount: Number(vars.sampleCount), xyPointLength: Number(vars.xyPointLength), @@ -425,7 +419,9 @@ export const SquigglePlayground: FC = ({ = ({ ); }; - -export function renderSquigglePlaygroundToDom(props: PlaygroundProps) { - const parent = document.createElement("div"); - ReactDOM.render(, parent); - return parent; -} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index de0b6dff..ce6e107c 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -1,14 +1,6 @@ export { SquiggleChart } from "./components/SquiggleChart"; -export { - SquiggleEditor, - SquigglePartial, - renderSquiggleEditorToDom, - renderSquigglePartialToDom, -} from "./components/SquiggleEditor"; -export { - SquigglePlayground, - renderSquigglePlaygroundToDom, -} from "./components/SquigglePlayground"; +export { SquiggleEditor, SquigglePartial } from "./components/SquiggleEditor"; +export { SquigglePlayground } from "./components/SquigglePlayground"; export { SquiggleContainer } from "./components/SquiggleContainer"; export { mergeBindings } from "@quri/squiggle-lang"; diff --git a/packages/components/src/lib/hooks.ts b/packages/components/src/lib/hooks.ts index 42db01ce..03b8c69d 100644 --- a/packages/components/src/lib/hooks.ts +++ b/packages/components/src/lib/hooks.ts @@ -5,7 +5,7 @@ import { run, runPartial, } from "@quri/squiggle-lang"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo } from "react"; type SquiggleArgs> = { code: string; @@ -19,37 +19,18 @@ const useSquiggleAny = >( args: SquiggleArgs, f: (...args: Parameters) => T ) => { - // We're using observable, where div elements can have a `value` property: - // https://observablehq.com/@observablehq/introduction-to-views - // - // This is here to get the 'viewof' part of: - // viewof env = cell('normal(0,1)') - // to work - const ref = useRef< - HTMLDivElement & { value?: Extract["value"] } - >(null); const result: T = useMemo( () => f(args.code, args.bindings, args.environment, args.jsImports), [f, args.code, args.bindings, args.environment, args.jsImports] ); - useEffect(() => { - if (!ref.current) return; - ref.current.value = result.tag === "Ok" ? result.value : undefined; - - ref.current.dispatchEvent(new CustomEvent("input")); - }, [result]); - const { onChange } = args; useEffect(() => { onChange?.(result.tag === "Ok" ? result.value : undefined); }, [result, onChange]); - return { - result, // squiggleExpression or externalBindings - observableRef: ref, // can be passed to outermost
if you want to use your component as an observablehq's view - }; + return result; }; export const useSquigglePartial = ( diff --git a/packages/components/src/stories/SquiggleEditor.stories.mdx b/packages/components/src/stories/SquiggleEditor.stories.mdx index 3ae37bb3..7e8f7e66 100644 --- a/packages/components/src/stories/SquiggleEditor.stories.mdx +++ b/packages/components/src/stories/SquiggleEditor.stories.mdx @@ -14,7 +14,7 @@ the distribution. {Template.bind({})} @@ -27,7 +27,7 @@ You can also name variables like so: {Template.bind({})} diff --git a/packages/components/src/stories/SquigglePartial.stories.mdx b/packages/components/src/stories/SquigglePartial.stories.mdx index b4446402..c4f4814a 100644 --- a/packages/components/src/stories/SquigglePartial.stories.mdx +++ b/packages/components/src/stories/SquigglePartial.stories.mdx @@ -15,7 +15,7 @@ instead returns bindings that can be used by further Squiggle Editors. {Template.bind({})} @@ -36,12 +36,12 @@ instead returns bindings that can be used by further Squiggle Editors. <> diff --git a/packages/website/docs/Discussions/Bugs.mdx b/packages/website/docs/Discussions/Bugs.mdx index a39223a9..2c077eb0 100644 --- a/packages/website/docs/Discussions/Bugs.mdx +++ b/packages/website/docs/Discussions/Bugs.mdx @@ -16,7 +16,7 @@ If you take the pointwise mixture of two distributions with very different means In the following case, the mean of the mixture should be equal to the sum of the means of the parts. These are shown as the first two displayed variables. These variables diverge as the underlying distributions change. diff --git a/packages/website/docs/Guides/DistributionCreation.mdx b/packages/website/docs/Guides/DistributionCreation.mdx index 5e84b47f..8ca22294 100644 --- a/packages/website/docs/Guides/DistributionCreation.mdx +++ b/packages/website/docs/Guides/DistributionCreation.mdx @@ -22,22 +22,22 @@ If both values are above zero, a `lognormal` distribution is used. If not, a `no When 5 to 10 is entered, both numbers are positive, so it generates a lognormal distribution with 5th and 95th percentiles at 5 and 10. - + 5 to 10 does the same thing as to(5,10). - + When -5 to 5 is entered, there's negative values, so it generates a normal distribution. This has 5th and 95th percentiles at 5 and 10. - + It's very easy to generate distributions with very long tails. If this happens, you can click the "log x scale" box to view this using a log scale. - + @@ -76,16 +76,16 @@ The `mixture` mixes combines multiple distributions to create a mixture. You can - + - + - + - + @@ -111,7 +111,7 @@ The `mixture` mixes combines multiple distributions to create a mixture. You can In this case, I have a 20% chance of spending 0 time with it. I might estimate my hours with,

@@ -125,7 +125,7 @@ mx(hours_the_project_will_take, 0, [chance_of_doing_anything, 1 - chance_of_doin very wide, just in case they were dramatically off for some weird reason.

- + - + @@ -165,7 +165,7 @@ Creates a [log-normal distribution](https://en.wikipedia.org/wiki/Log-normal_dis you take the log of our lognormal distribution. They can be difficult to directly reason about. Because of this complexity, we recommend typically using the to syntax instead of estimating `mu` and `sigma` directly. - + ### Arguments @@ -185,7 +185,7 @@ Because of this complexity, we recommend typically using the to ) with the given low and high values. - + ### Arguments @@ -236,19 +236,19 @@ with values at 1 and 2. Therefore, this is the same as `mixture(pointMass(1),poi - + - + - + - + - + @@ -264,19 +264,19 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w - + - + - + - + - + @@ -295,16 +295,16 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w Examples - + - + - + - + @@ -316,7 +316,7 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w Creates an [exponential distribution](https://en.wikipedia.org/wiki/Exponential_distribution) with the given rate. - + ### Arguments @@ -334,7 +334,7 @@ Creates a [triangular distribution](https://en.wikipedia.org/wiki/Triangular_dis - `mode`: Number greater than `low` - `high`: Number greater than `mode` - + ## FromSamples @@ -342,7 +342,7 @@ Creates a [triangular distribution](https://en.wikipedia.org/wiki/Triangular_dis Creates a sample set distribution using an array of samples. - + ### Arguments diff --git a/packages/website/docs/Guides/Functions.mdx b/packages/website/docs/Guides/Functions.mdx index be579834..a58df345 100644 --- a/packages/website/docs/Guides/Functions.mdx +++ b/packages/website/docs/Guides/Functions.mdx @@ -16,7 +16,7 @@ the value of one random sample chosen from the first distribution and the value chosen from the second distribution. @@ -28,7 +28,7 @@ the distribution of the value of one random sample chosen from the first distrib the value of one random sample chosen from the second distribution. @@ -40,14 +40,14 @@ the value of one random sample chosen from the first distribution times the valu chosen from the second distribution. We also provide concatenation of two distributions as a syntax sugar for `*` - + ### Division @@ -58,7 +58,7 @@ chosen from the second distribution. If the second distribution has some values tends to be particularly unstable. @@ -69,12 +69,12 @@ A projection over a contracted x-axis. The exponentiation operation represents t the exponentiation of the value of one random sample chosen from the first distribution to the power of the value one random sample chosen from the second distribution. - + ### Taking the base `e` exponential @@ -83,19 +83,19 @@ exp(dist)`} A projection over a stretched x-axis. Base `x` @@ -114,7 +114,7 @@ For every point on the x-axis, operate the corresponding points in the y axis of TODO: this isn't in the new interpreter/parser yet. @@ -124,7 +124,7 @@ dist1 .+ dist2`} TODO: this isn't in the new interpreter/parser yet. @@ -132,7 +132,7 @@ dist1 .- dist2`} ### Pointwise multiplication @@ -140,7 +140,7 @@ dist1 .* dist2`} ### Pointwise division @@ -148,7 +148,7 @@ dist1 ./ dist2`} ### Pointwise exponentiation @@ -160,7 +160,7 @@ dist1 .^ dist2`} The `pdf(dist, x)` function returns the density of a distribution at the given point x. - + #### Validity @@ -172,7 +172,7 @@ given point x. The `cdf(dist, x)` gives the cumulative probability of the distribution or all values lower than x. It is the inverse of `quantile`. - + #### Validity @@ -185,7 +185,7 @@ The `quantile(dist, prob)` gives the value x or which the probability for all va lower than x is equal to prob. It is the inverse of `cdf`. In the literature, it is also known as the quantiles function. - + #### Validity @@ -196,29 +196,29 @@ is also known as the quantiles function. The `mean(distribution)` function gives the mean (expected value) of a distribution. - + ### Sampling a distribution The `sample(distribution)` samples a given distribution. - + ## Converting between distribution formats Recall the [three formats of distributions](https://develop--squiggle-documentation.netlify.app/docs/Discussions/Three-Types-Of-Distributions). We can force any distribution into `SampleSet` format - + Or `PointSet` format - + ### `toSampleSet` has two signatures Above, we saw the unary `toSampleSet`, which uses an internal hardcoded number of samples. If you'd like to provide the number of samples, it has a binary signature as well (floored) - + #### Validity @@ -230,13 +230,13 @@ Some distribution operations (like horizontal shift) return an unnormalized dist We provide a `normalize` function - + #### Validity - Input to `normalize` must be a dist We provide a predicate `isNormalized`, for when we have simple control flow - + #### Validity @@ -246,7 +246,7 @@ We provide a predicate `isNormalized`, for when we have simple control flow You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. - + Save for a logging side effect, `inspect` does nothing to input and returns it. @@ -254,12 +254,12 @@ Save for a logging side effect, `inspect` does nothing to input and returns it. You can cut off from the left - + You can cut off from the right - + You can cut off from both sides - + diff --git a/packages/website/docs/Guides/Language.mdx b/packages/website/docs/Guides/Language.mdx index 4cfd9ea8..bdc58e41 100644 --- a/packages/website/docs/Guides/Language.mdx +++ b/packages/website/docs/Guides/Language.mdx @@ -9,22 +9,22 @@ import { SquiggleEditor } from "../../src/components/SquiggleEditor"; ### Distributions - + ### Numbers - + ### Arrays ### Records @@ -33,7 +33,7 @@ d.dist`} A statement assigns expressions to names. It looks like ` = ` @@ -42,7 +42,7 @@ A statement assigns expressions to names. It looks like ` = We can define functions diff --git a/yarn.lock b/yarn.lock index ae8a6f60..d7c13cbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4311,7 +4311,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.5": +"@types/react-dom@^18.0.0": version "18.0.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a" integrity sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA== From 06f86963d1fc370daf505b38166e615199ee28d5 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 24 Jun 2022 05:10:28 +0000 Subject: [PATCH 02/29] Update documentation for squiggleString --- packages/components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/README.md b/packages/components/README.md index a74ee32d..bc8d3123 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -20,7 +20,7 @@ Add to `App.js`: ```jsx import { SquiggleEditor } from "@quri/squiggle-components"; ; ``` From 69aa8a8cc177477e322d483b584ff0de633a4f28 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Mon, 27 Jun 2022 07:07:09 +0000 Subject: [PATCH 03/29] Add controlled and uncontrolled versions of components --- .../src/components/SquiggleChart.tsx | 6 +- .../src/components/SquiggleEditor.tsx | 49 ++++++++++---- .../src/components/SquigglePlayground.tsx | 10 ++- .../src/stories/SquiggleChart.stories.mdx | 27 ++++---- .../src/stories/SquiggleEditor.stories.mdx | 4 +- .../src/stories/SquigglePartial.stories.mdx | 6 +- packages/website/docs/Discussions/Bugs.mdx | 4 +- .../docs/Guides/DistributionCreation.mdx | 64 +++++++++---------- packages/website/docs/Guides/Functions.mdx | 58 ++++++++--------- packages/website/docs/Guides/Language.mdx | 12 ++-- packages/website/src/pages/playground.js | 2 +- 11 files changed, 131 insertions(+), 111 deletions(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 3e712396..9e352a62 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -14,7 +14,7 @@ import { SquiggleItem } from "./SquiggleItem"; export interface SquiggleChartProps { /** The input string for squiggle */ - squiggleString?: string; + code?: string; /** If the output requires monte carlo sampling, the amount of samples */ sampleCount?: number; /** The amount of points returned to draw the distribution */ @@ -49,7 +49,7 @@ export interface SquiggleChartProps { const defaultOnChange = () => {}; export const SquiggleChart: React.FC = ({ - squiggleString = "", + code = "", environment, onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here height = 200, @@ -66,7 +66,7 @@ export const SquiggleChart: React.FC = ({ diagramCount = 100, }) => { const result = useSquiggle({ - code: squiggleString, + code, bindings, environment, jsImports, diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 3d0e0db5..78aec513 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -22,27 +22,41 @@ const WrappedCodeEditor: React.FC<{
); -export type SquiggleEditorProps = SquiggleChartProps; +export type SquiggleEditorProps = SquiggleChartProps & { + defaultCode?: string; + onCodeChange?: (code: string) => void; +}; export const SquiggleEditor: React.FC = (props) => { - const { squiggleString = "" } = props; - const [code, setCode] = useState(squiggleString); - React.useEffect(() => setCode(squiggleString), [squiggleString]); + let defaultCode = props.defaultCode ?? ""; + const [uncontrolledCode, setCode] = useState(defaultCode); + let code = props.code ?? uncontrolledCode; - let chartProps = { ...props, squiggleString: code }; + let chartProps = { ...props, code }; return ( - + { + if (props.onCodeChange) props.onCodeChange(code); + + if (props.code === undefined) setCode(code); + }} + /> ); }; export interface SquigglePartialProps { - /** The input string for squiggle */ - squiggleString?: string; + /** 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 */ @@ -52,17 +66,19 @@ export interface SquigglePartialProps { } export const SquigglePartial: React.FC = ({ - squiggleString = "", + code, + defaultCode = "", onChange, + onCodeChange, bindings = defaultBindings, environment, jsImports = defaultImports, }: SquigglePartialProps) => { - const [code, setCode] = useState(squiggleString); - React.useEffect(() => setCode(squiggleString), [squiggleString]); + const [uncontrolledCode, setCode] = useState(defaultCode); + let codeProp = code ?? uncontrolledCode; const result = useSquigglePartial({ - code, + code: codeProp, bindings, environment, jsImports, @@ -71,7 +87,14 @@ export const SquigglePartial: React.FC = ({ return ( - + { + if (onCodeChange) onCodeChange(code); + + if (code === undefined) setCode(code); + }} + /> {result.tag !== "Ok" ? : null} ); diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index ce59b5a4..e96a1053 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -22,7 +22,7 @@ import { SquiggleContainer } from "./SquiggleContainer"; interface PlaygroundProps { /** The initial squiggle string to put in the playground */ - initialSquiggleString?: string; + defaultCode?: string; /** How many pixels high is the playground */ height?: number; /** Whether to show the types of outputs in the playground */ @@ -204,7 +204,7 @@ function Checkbox({ } export const SquigglePlayground: FC = ({ - initialSquiggleString = "", + defaultCode = "", height = 500, showTypes = false, showControls = false, @@ -216,9 +216,7 @@ export const SquigglePlayground: FC = ({ onSettingsChange, showEditor = true, }) => { - const [uncontrolledCode, setUncontrolledCode] = useState( - initialSquiggleString - ); + const [uncontrolledCode, setUncontrolledCode] = useState(defaultCode); const [importString, setImportString] = useState("{}"); const [imports, setImports] = useState({}); const [importsAreValid, setImportsAreValid] = useState(true); @@ -417,7 +415,7 @@ export const SquigglePlayground: FC = ({ const squiggleChart = ( @@ -43,7 +43,7 @@ could be continuous, discrete or mixed. @@ -57,7 +57,7 @@ could be continuous, discrete or mixed. @@ -71,7 +71,7 @@ could be continuous, discrete or mixed. @@ -85,8 +85,7 @@ could be continuous, discrete or mixed. @@ -103,7 +102,7 @@ to allow large and small numbers being printed cleanly. @@ -117,7 +116,7 @@ to allow large and small numbers being printed cleanly. @@ -131,7 +130,7 @@ to allow large and small numbers being printed cleanly. @@ -145,7 +144,7 @@ to allow large and small numbers being printed cleanly. @@ -159,7 +158,7 @@ to allow large and small numbers being printed cleanly. @@ -173,7 +172,7 @@ to allow large and small numbers being printed cleanly. @@ -187,7 +186,7 @@ to allow large and small numbers being printed cleanly. @@ -201,7 +200,7 @@ to allow large and small numbers being printed cleanly. diff --git a/packages/components/src/stories/SquiggleEditor.stories.mdx b/packages/components/src/stories/SquiggleEditor.stories.mdx index 7e8f7e66..e51e3e33 100644 --- a/packages/components/src/stories/SquiggleEditor.stories.mdx +++ b/packages/components/src/stories/SquiggleEditor.stories.mdx @@ -14,7 +14,7 @@ the distribution. {Template.bind({})} @@ -27,7 +27,7 @@ You can also name variables like so: {Template.bind({})} diff --git a/packages/components/src/stories/SquigglePartial.stories.mdx b/packages/components/src/stories/SquigglePartial.stories.mdx index c4f4814a..3305586b 100644 --- a/packages/components/src/stories/SquigglePartial.stories.mdx +++ b/packages/components/src/stories/SquigglePartial.stories.mdx @@ -15,7 +15,7 @@ instead returns bindings that can be used by further Squiggle Editors. {Template.bind({})} @@ -36,12 +36,12 @@ instead returns bindings that can be used by further Squiggle Editors. <> diff --git a/packages/website/docs/Discussions/Bugs.mdx b/packages/website/docs/Discussions/Bugs.mdx index 2c077eb0..db53a6f6 100644 --- a/packages/website/docs/Discussions/Bugs.mdx +++ b/packages/website/docs/Discussions/Bugs.mdx @@ -16,7 +16,7 @@ If you take the pointwise mixture of two distributions with very different means In the following case, the mean of the mixture should be equal to the sum of the means of the parts. These are shown as the first two displayed variables. These variables diverge as the underlying distributions change. diff --git a/packages/website/docs/Guides/DistributionCreation.mdx b/packages/website/docs/Guides/DistributionCreation.mdx index 8ca22294..7ef80d0a 100644 --- a/packages/website/docs/Guides/DistributionCreation.mdx +++ b/packages/website/docs/Guides/DistributionCreation.mdx @@ -22,22 +22,22 @@ If both values are above zero, a `lognormal` distribution is used. If not, a `no When 5 to 10 is entered, both numbers are positive, so it generates a lognormal distribution with 5th and 95th percentiles at 5 and 10. - + 5 to 10 does the same thing as to(5,10). - + When -5 to 5 is entered, there's negative values, so it generates a normal distribution. This has 5th and 95th percentiles at 5 and 10. - + It's very easy to generate distributions with very long tails. If this happens, you can click the "log x scale" box to view this using a log scale. - + @@ -76,16 +76,16 @@ The `mixture` mixes combines multiple distributions to create a mixture. You can - + - + - + - + @@ -111,7 +111,7 @@ The `mixture` mixes combines multiple distributions to create a mixture. You can In this case, I have a 20% chance of spending 0 time with it. I might estimate my hours with,

@@ -125,7 +125,7 @@ mx(hours_the_project_will_take, 0, [chance_of_doing_anything, 1 - chance_of_doin very wide, just in case they were dramatically off for some weird reason.

- + - + @@ -165,7 +165,7 @@ Creates a [log-normal distribution](https://en.wikipedia.org/wiki/Log-normal_dis you take the log of our lognormal distribution. They can be difficult to directly reason about. Because of this complexity, we recommend typically using the to syntax instead of estimating `mu` and `sigma` directly. - + ### Arguments @@ -185,7 +185,7 @@ Because of this complexity, we recommend typically using the to ) with the given low and high values. - + ### Arguments @@ -236,19 +236,19 @@ with values at 1 and 2. Therefore, this is the same as `mixture(pointMass(1),poi - + - + - + - + - + @@ -264,19 +264,19 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w - + - + - + - + - + @@ -295,16 +295,16 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w Examples - + - + - + - + @@ -316,7 +316,7 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w Creates an [exponential distribution](https://en.wikipedia.org/wiki/Exponential_distribution) with the given rate. - + ### Arguments @@ -334,7 +334,7 @@ Creates a [triangular distribution](https://en.wikipedia.org/wiki/Triangular_dis - `mode`: Number greater than `low` - `high`: Number greater than `mode` - + ## FromSamples @@ -342,7 +342,7 @@ Creates a [triangular distribution](https://en.wikipedia.org/wiki/Triangular_dis Creates a sample set distribution using an array of samples. - + ### Arguments diff --git a/packages/website/docs/Guides/Functions.mdx b/packages/website/docs/Guides/Functions.mdx index a58df345..a428ac7c 100644 --- a/packages/website/docs/Guides/Functions.mdx +++ b/packages/website/docs/Guides/Functions.mdx @@ -16,7 +16,7 @@ the value of one random sample chosen from the first distribution and the value chosen from the second distribution. @@ -28,7 +28,7 @@ the distribution of the value of one random sample chosen from the first distrib the value of one random sample chosen from the second distribution. @@ -40,14 +40,14 @@ the value of one random sample chosen from the first distribution times the valu chosen from the second distribution. We also provide concatenation of two distributions as a syntax sugar for `*` - + ### Division @@ -58,7 +58,7 @@ chosen from the second distribution. If the second distribution has some values tends to be particularly unstable. @@ -69,12 +69,12 @@ A projection over a contracted x-axis. The exponentiation operation represents t the exponentiation of the value of one random sample chosen from the first distribution to the power of the value one random sample chosen from the second distribution. - + ### Taking the base `e` exponential @@ -83,19 +83,19 @@ exp(dist)`} A projection over a stretched x-axis. Base `x` @@ -114,7 +114,7 @@ For every point on the x-axis, operate the corresponding points in the y axis of TODO: this isn't in the new interpreter/parser yet. @@ -124,7 +124,7 @@ dist1 .+ dist2`} TODO: this isn't in the new interpreter/parser yet. @@ -132,7 +132,7 @@ dist1 .- dist2`} ### Pointwise multiplication @@ -140,7 +140,7 @@ dist1 .* dist2`} ### Pointwise division @@ -148,7 +148,7 @@ dist1 ./ dist2`} ### Pointwise exponentiation @@ -160,7 +160,7 @@ dist1 .^ dist2`} The `pdf(dist, x)` function returns the density of a distribution at the given point x. - + #### Validity @@ -172,7 +172,7 @@ given point x. The `cdf(dist, x)` gives the cumulative probability of the distribution or all values lower than x. It is the inverse of `quantile`. - + #### Validity @@ -185,7 +185,7 @@ The `quantile(dist, prob)` gives the value x or which the probability for all va lower than x is equal to prob. It is the inverse of `cdf`. In the literature, it is also known as the quantiles function. - + #### Validity @@ -196,29 +196,29 @@ is also known as the quantiles function. The `mean(distribution)` function gives the mean (expected value) of a distribution. - + ### Sampling a distribution The `sample(distribution)` samples a given distribution. - + ## Converting between distribution formats Recall the [three formats of distributions](https://develop--squiggle-documentation.netlify.app/docs/Discussions/Three-Types-Of-Distributions). We can force any distribution into `SampleSet` format - + Or `PointSet` format - + ### `toSampleSet` has two signatures Above, we saw the unary `toSampleSet`, which uses an internal hardcoded number of samples. If you'd like to provide the number of samples, it has a binary signature as well (floored) - + #### Validity @@ -230,13 +230,13 @@ Some distribution operations (like horizontal shift) return an unnormalized dist We provide a `normalize` function - + #### Validity - Input to `normalize` must be a dist We provide a predicate `isNormalized`, for when we have simple control flow - + #### Validity @@ -246,7 +246,7 @@ We provide a predicate `isNormalized`, for when we have simple control flow You may like to debug by right clicking your browser and using the _inspect_ functionality on the webpage, and viewing the _console_ tab. Then, wrap your squiggle output with `inspect` to log an internal representation. - + Save for a logging side effect, `inspect` does nothing to input and returns it. @@ -254,12 +254,12 @@ Save for a logging side effect, `inspect` does nothing to input and returns it. You can cut off from the left - + You can cut off from the right - + You can cut off from both sides - + diff --git a/packages/website/docs/Guides/Language.mdx b/packages/website/docs/Guides/Language.mdx index bdc58e41..2f26cdd6 100644 --- a/packages/website/docs/Guides/Language.mdx +++ b/packages/website/docs/Guides/Language.mdx @@ -9,22 +9,22 @@ import { SquiggleEditor } from "../../src/components/SquiggleEditor"; ### Distributions - + ### Numbers - + ### Arrays ### Records @@ -33,7 +33,7 @@ d.dist`} A statement assigns expressions to names. It looks like ` = ` @@ -42,7 +42,7 @@ A statement assigns expressions to names. It looks like ` = We can define functions diff --git a/packages/website/src/pages/playground.js b/packages/website/src/pages/playground.js index 48a2c90d..70904eba 100644 --- a/packages/website/src/pages/playground.js +++ b/packages/website/src/pages/playground.js @@ -37,7 +37,7 @@ function setHashData(data) { export default function PlaygroundPage() { const playgroundProps = { - initialSquiggleString: "normal(0,1)", + defaultCode: "normal(0,1)", height: 700, showTypes: true, ...getHashData(), From 14189fd8484cae8d6484f7d46755a090a6c19ab9 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 27 Jun 2022 14:31:35 -0400 Subject: [PATCH 04/29] #497 (no tests or examples) --- .../Distributions/SymbolicDist/SymbolicDist.res | 13 +++++++++++++ .../FunctionRegistry/FunctionRegistry_Library.res | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index f33fc450..762e5fd5 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -124,6 +124,19 @@ module Beta = { let sample = (t: t) => Jstat.Beta.sample(t.alpha, t.beta) let mean = (t: t) => Ok(Jstat.Beta.mean(t.alpha, t.beta)) let toString = ({alpha, beta}: t) => j`Beta($alpha,$beta)` + + let fromMeanAndSampleSize = (mean, sampleSize) => { + // https://en.wikipedia.org/wiki/Beta_distribution#Mean_and_sample_size + let alpha = mean *. sampleSize + let beta = (1.0 -. mean) *. sampleSize + alpha->make(beta) + } + + let fromMeanAndStdev = (mean, stdev) => { + let var = stdev *. stdev + let sampleSize = mean *. (1.0 -. mean) /. var + mean->fromMeanAndSampleSize(sampleSize) + } } module Lognormal = { diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index 56315860..a37b8dc4 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -117,8 +117,12 @@ lognormal({mean: 5, stdev: 2})`, ), Function.make( ~name="Beta", - ~examples=`beta(20, 25)`, - ~definitions=[TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make))], + ~examples=`beta(20, 25) +beta({mean: 0.39, stdev: 0.1})`, + ~definitions=[ + TwoArgDist.make("beta", twoArgs(SymbolicDist.Beta.make)), + TwoArgDist.makeRecordMeanStdev("beta", twoArgs(SymbolicDist.Beta.fromMeanAndStdev)), + ], (), ), Function.make( From 54cd6365241e63671189a70c5cdbe8cb40e1bb77 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 27 Jun 2022 14:37:12 -0400 Subject: [PATCH 05/29] added comment for more direct wikipedia citation --- .../src/rescript/Distributions/SymbolicDist/SymbolicDist.res | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index 762e5fd5..61470d08 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -133,6 +133,7 @@ module Beta = { } let fromMeanAndStdev = (mean, stdev) => { + // https://en.wikipedia.org/wiki/Beta_distribution#Mean_and_variance let var = stdev *. stdev let sampleSize = mean *. (1.0 -. mean) /. var mean->fromMeanAndSampleSize(sampleSize) From 9dcac8dd481b3099cf2fe0b73705248c8e74f5b2 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 30 Jun 2022 12:20:50 -0400 Subject: [PATCH 06/29] fixed \`-1\` I had forgotten; added basic mean tests --- .../__tests__/Distributions/Symbolic_test.res | 17 +++++++++++++++++ .../Distributions/SymbolicDist/SymbolicDist.res | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res index 6ee9ffc8..152b580d 100644 --- a/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Distributions/Symbolic_test.res @@ -77,6 +77,23 @@ describe("(Symbolic) mean", () => { meanValue->unpackFloat->expect->ExpectJs.toBeFalsy }) + testAll( + "of beta distributions from mean and standard dev", + list{(0.39, 0.1), (0.08, 0.1), (0.8, 0.3)}, + tup => { + let (mean, stdev) = tup + let betaDistribution = SymbolicDist.Beta.fromMeanAndStdev(mean, stdev) + let meanValue = + betaDistribution->E.R2.fmap(d => + run(FromDist(ToFloat(#Mean), d->DistributionTypes.Symbolic)) + ) + switch meanValue { + | Ok(value) => value->unpackFloat->expect->toBeCloseTo(mean) + | Error(err) => err->expect->toBe("shouldn't happen") + } + }, + ) + testAll( "of lognormal distributions", list{(2.0, 4.0), (1e-7, 1e-2), (-1e6, 10.0), (1e3, -1e2), (-1e8, -1e4), (1e2, 1e-5)}, diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index 61470d08..0b192b5f 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -135,7 +135,7 @@ module Beta = { let fromMeanAndStdev = (mean, stdev) => { // https://en.wikipedia.org/wiki/Beta_distribution#Mean_and_variance let var = stdev *. stdev - let sampleSize = mean *. (1.0 -. mean) /. var + let sampleSize = mean *. (1.0 -. mean) /. var -. 1.0 mean->fromMeanAndSampleSize(sampleSize) } } From e0289ed23fea20006e5b89f8914f7a2a97efdc58 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 30 Jun 2022 12:29:09 -0400 Subject: [PATCH 07/29] documented addition to `beta` api --- packages/website/docs/Api/DistGeneric.mdx | 2 ++ packages/website/docs/Guides/DistributionCreation.mdx | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/packages/website/docs/Api/DistGeneric.mdx b/packages/website/docs/Api/DistGeneric.mdx index e49eec1c..42f464df 100644 --- a/packages/website/docs/Api/DistGeneric.mdx +++ b/packages/website/docs/Api/DistGeneric.mdx @@ -70,12 +70,14 @@ uniform(10, 12); ``` beta: (distribution|number, distribution|number) => distribution +beta: (dict<{mean: distribution|number, stdev: distribution|number}>) => distribution ``` **Examples** ```javascript beta(20, 25); +beta({ mean: 0.39, stdev: 0.1 }); ``` ### cauchy diff --git a/packages/website/docs/Guides/DistributionCreation.mdx b/packages/website/docs/Guides/DistributionCreation.mdx index 5e84b47f..1ad69960 100644 --- a/packages/website/docs/Guides/DistributionCreation.mdx +++ b/packages/website/docs/Guides/DistributionCreation.mdx @@ -259,6 +259,7 @@ with values at 1 and 2. Therefore, this is the same as `mixture(pointMass(1),poi ## Beta `beta(alpha:number, beta:number)` +`beta({mean: number, stdev: number})` Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) with the given `alpha` and `beta` values. For a good summary of the beta distribution, see [this explanation](https://stats.stackexchange.com/a/47782) on Stack Overflow. @@ -278,6 +279,12 @@ Creates a [beta distribution](https://en.wikipedia.org/wiki/Beta_distribution) w + + + ### Arguments From 5b41019c04d4b5137af7776bc21b84e784635371 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Thu, 30 Jun 2022 14:38:59 -0400 Subject: [PATCH 08/29] hotfix: relative path --- packages/website/docs/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/docs/Introduction.md b/packages/website/docs/Introduction.md index 35ff63b6..e04157c2 100644 --- a/packages/website/docs/Introduction.md +++ b/packages/website/docs/Introduction.md @@ -10,7 +10,7 @@ Squiggle is an _estimation language_, and a syntax for _calculating and expressi - [Gallery](./Discussions/Gallery) - [Squiggle playground](/playground) - [Language basics](./Guides/Language) -- [Squiggle functions source of truth](./docs/Guides/Functions) +- [Squiggle functions source of truth](./Guides/Functions) - [Known bugs](./Discussions/Bugs) - [Original lesswrong sequence](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3) - [Author your squiggle models as Observable notebooks](https://observablehq.com/@hazelfire/squiggle) From 38cc93bdec86ab7bf100bb9b6520bdb88279de59 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 27 Jun 2022 02:40:31 +0200 Subject: [PATCH 09/29] Module.defineFFI --- .../__tests__/Reducer/Reducer_Helpers.res | 2 +- .../Reducer/Reducer_TestMacroHelpers.res | 2 +- .../Reducer_Category_Module_TypeChecker.res | 16 ---- .../Reducer_Dispatch_BuiltIn.res | 84 ++++-------------- .../Reducer_Dispatch_BuiltInMacros.res | 2 +- .../rescript/Reducer/Reducer_Exception.res | 3 + .../Reducer_Expression/Reducer_Expression.res | 2 +- .../Reducer_ExpressionWithContext.res | 2 +- .../Reducer_Expression_BindingsReplacer.res | 2 +- .../Reducer_Expression_ExpressionBuilder.res | 18 ++-- .../Reducer_Expression_Lambda.res | 2 +- .../Reducer_Module.res} | 17 +++- .../Reducer_Module/Reducer_Module_Lambda.res | 29 ++++++ .../Reducer_Peggy_GeneratedParser.peggy | 15 ++-- .../Reducer_Peggy/Reducer_Peggy_Parse.res | 6 +- .../Reducer/Reducer_Type/Reducer_Type_T.res | 82 +++++++++++++++++ .../Reducer_Type/Reducer_Type_TypeBuilder.res | 88 +++++++++++++++++++ .../Reducer_Type/Reducer_Type_TypeChecker.res | 81 +++++++++++++++++ ...ducerInterface_ExternalExpressionValue.res | 81 ++--------------- ...ducerInterface_InternalExpressionValue.res | 62 +++++++------ .../ReducerInterface_StdLib.res | 2 +- .../SquiggleLibrary/SquiggleLibrary_Math.res | 4 +- 22 files changed, 388 insertions(+), 214 deletions(-) delete mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module_TypeChecker.res create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Exception.res rename packages/squiggle-lang/src/rescript/Reducer/{Reducer_Category/Reducer_Category_Module.res => Reducer_Module/Reducer_Module.res} (87%) create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res index 8e3d8257..4fb1382f 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res @@ -2,7 +2,7 @@ module ErrorValue = Reducer_ErrorValue module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue module InternalExpressionValue = ReducerInterface.InternalExpressionValue -module Module = Reducer_Category_Module +module Module = Reducer_Module let removeDefaultsInternal = (iev: InternalExpressionValue.t) => { switch iev { diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res index 103609a2..2337dde2 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res @@ -8,7 +8,7 @@ module InternalExpressionValue = ReducerInterface.InternalExpressionValue module ExpressionWithContext = Reducer_ExpressionWithContext module Macro = Reducer_Expression_Macro module T = Reducer_Expression_T -module Module = Reducer_Category_Module +module Module = Reducer_Module let testMacro_ = ( tester, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module_TypeChecker.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module_TypeChecker.res deleted file mode 100644 index 957b0dc8..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module_TypeChecker.res +++ /dev/null @@ -1,16 +0,0 @@ -module ExpressionValue = ReducerInterface_InternalExpressionValue -module ExpressionT = Reducer_Expression_T -open ExpressionValue - -let isOfResolvedType = (aType, aValue) => { - let caseTypeIdentifier = (aTypeIdentifier0, aValue) => { - let valueType = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase - let aTypeIdentifier = aTypeIdentifier0->Js.String2.toLowerCase - aTypeIdentifier === valueType - } - - switch aType { - | IEvTypeIdentifier(aTypeIdentifier) => caseTypeIdentifier(aTypeIdentifier, aValue) - | _ => false - } -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res index 6012d179..484b0acb 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res @@ -3,8 +3,9 @@ module ExpressionT = Reducer_Expression_T module ExternalLibrary = ReducerInterface.ExternalLibrary module Lambda = Reducer_Expression_Lambda module MathJs = Reducer_MathJs -module Module = Reducer_Category_Module +module Module = Reducer_Module module Result = Belt.Result +module TypeBuilder = Reducer_Type_TypeBuilder open ReducerInterface_InternalExpressionValue open Reducer_ErrorValue @@ -166,59 +167,6 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce ) } - let typeModifier_memberOf = (aType, anArray) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("memberOf", anArray)->IEvRecord->Ok - } - let typeModifier_memberOf_update = (aRecord, anArray) => { - aRecord->Belt.Map.String.set("memberOf", anArray)->IEvRecord->Ok - } - - let typeModifier_min = (aType, value) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("min", value)->IEvRecord->Ok - } - let typeModifier_min_update = (aRecord, value) => { - aRecord->Belt.Map.String.set("min", value)->IEvRecord->Ok - } - - let typeModifier_max = (aType, value) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("max", value)->IEvRecord->Ok - } - let typeModifier_max_update = (aRecord, value) => - aRecord->Belt.Map.String.set("max", value)->IEvRecord->Ok - - let typeModifier_opaque_update = aRecord => - aRecord->Belt.Map.String.set("opaque", IEvBool(true))->IEvRecord->Ok - - let typeOr = evArray => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeOr")), - ("typeOr", evArray), - ]) - newRecord->IEvRecord->Ok - } - let typeFunction = anArray => { - let output = Belt.Array.getUnsafe(anArray, Js.Array2.length(anArray) - 1) - let inputs = Js.Array2.slice(anArray, ~start=0, ~end_=-1) - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeFunction")), - ("inputs", IEvArray(inputs)), - ("output", output), - ]) - newRecord->IEvRecord->Ok - } - switch call { | ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex) | ("$_atIndex_$", [IEvModule(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex) @@ -233,20 +181,24 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce | ("$_setTypeOfBindings_$", [IEvModule(nameSpace), IEvSymbol(symbol), value]) => doSetTypeOfBindings(nameSpace, symbol, value) | ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) => - typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr)) - | ("$_typeModifier_memberOf_$", [IEvRecord(typeRecord), IEvArray(arr)]) => - typeModifier_memberOf_update(typeRecord, IEvArray(arr)) + TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr)) + | ("$_typeModifier_memberOf_$", [IEvType(typeRecord), IEvArray(arr)]) => + TypeBuilder.typeModifier_memberOf_update(typeRecord, IEvArray(arr)) | ("$_typeModifier_min_$", [IEvTypeIdentifier(typeIdentifier), value]) => - typeModifier_min(IEvTypeIdentifier(typeIdentifier), value) - | ("$_typeModifier_min_$", [IEvRecord(typeRecord), value]) => - typeModifier_min_update(typeRecord, value) + TypeBuilder.typeModifier_min(IEvTypeIdentifier(typeIdentifier), value) + | ("$_typeModifier_min_$", [IEvType(typeRecord), value]) => + TypeBuilder.typeModifier_min_update(typeRecord, value) | ("$_typeModifier_max_$", [IEvTypeIdentifier(typeIdentifier), value]) => - typeModifier_max(IEvTypeIdentifier(typeIdentifier), value) - | ("$_typeModifier_max_$", [IEvRecord(typeRecord), value]) => - typeModifier_max_update(typeRecord, value) - | ("$_typeModifier_opaque_$", [IEvRecord(typeRecord)]) => typeModifier_opaque_update(typeRecord) - | ("$_typeOr_$", [IEvArray(arr)]) => typeOr(IEvArray(arr)) - | ("$_typeFunction_$", [IEvArray(arr)]) => typeFunction(arr) + TypeBuilder.typeModifier_max(IEvTypeIdentifier(typeIdentifier), value) + | ("$_typeModifier_max_$", [IEvType(typeRecord), value]) => + TypeBuilder.typeModifier_max_update(typeRecord, value) + | ("$_typeModifier_opaque_$", [IEvType(typeRecord)]) => + TypeBuilder.typeModifier_opaque_update(typeRecord) + | ("$_typeOr_$", [IEvArray(arr)]) => TypeBuilder.typeOr(IEvArray(arr)) + | ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr) + | ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems) + | ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem) + | ("$_typeRecord_$", [IEvArray(arrayOfPairs)]) => TypeBuilder.typeRecord(arrayOfPairs) | ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) => doAddArray(aValueArray, bValueArray) | ("concat", [IEvString(aValueString), IEvString(bValueString)]) => diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index 1467f815..e0e6902e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -9,7 +9,7 @@ module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T module InternalExpressionValue = ReducerInterface_InternalExpressionValue module ExpressionWithContext = Reducer_ExpressionWithContext -module Module = Reducer_Category_Module +module Module = Reducer_Module module Result = Belt.Result open Reducer_Expression_ExpressionBuilder diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Exception.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Exception.res new file mode 100644 index 00000000..d7ca335c --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Exception.res @@ -0,0 +1,3 @@ +// There are switch stament cases in the code which are impossible to reach by design. +// ImpossibleException is a sign of programming error. +exception ImpossibleException diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index e7c82e77..9df4ff28 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -6,7 +6,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Lambda = Reducer_Expression_Lambda module Macro = Reducer_Expression_Macro module MathJs = Reducer_MathJs -module Module = Reducer_Category_Module +module Module = Reducer_Module module Result = Belt.Result module T = Reducer_Expression_T diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res index 1f6b18b7..7ab1ba05 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res @@ -3,7 +3,7 @@ module ErrorValue = Reducer_ErrorValue module ExpressionT = Reducer_Expression_T module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Result = Belt.Result -module Module = Reducer_Category_Module +module Module = Reducer_Module type bindings = ExpressionT.bindings type context = bindings diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res index e96ae4a1..6e1ef774 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res @@ -2,7 +2,7 @@ module ErrorValue = Reducer_ErrorValue module ExpressionT = Reducer_Expression_T module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Result = Belt.Result -module Module = Reducer_Category_Module +module Module = Reducer_Module type errorValue = Reducer_ErrorValue.errorValue type expression = ExpressionT.expression diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index 9fd4a34a..590cc99f 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -2,7 +2,7 @@ module BBindingsReplacer = Reducer_Expression_BindingsReplacer module BErrorValue = Reducer_ErrorValue module BExpressionT = Reducer_Expression_T module BInternalExpressionValue = ReducerInterface_InternalExpressionValue -module BModule = Reducer_Category_Module +module BModule = Reducer_Module type errorValue = BErrorValue.errorValue type expression = BExpressionT.expression @@ -10,8 +10,6 @@ type expressionOrFFI = BExpressionT.expressionOrFFI type ffiFn = BExpressionT.ffiFn type internalCode = ReducerInterface_InternalExpressionValue.internalCode -external castExpressionToInternalCode: expressionOrFFI => internalCode = "%identity" - let eArray = anArray => anArray->BInternalExpressionValue.IEvArray->BExpressionT.EValue let eArrayString = anArray => anArray->BInternalExpressionValue.IEvArrayString->BExpressionT.EValue @@ -37,17 +35,12 @@ let eLambda = ( BInternalExpressionValue.IEvLambda({ parameters: parameters, context: context, - body: NotFFI(expr)->castExpressionToInternalCode, + body: NotFFI(expr)->BModule.castExpressionToInternalCode, })->BExpressionT.EValue } -let eLambdaFFI = (parameters: array, ffiFn: ffiFn) => { - let context = BModule.emptyModule - BInternalExpressionValue.IEvLambda({ - parameters: parameters, - context: context, - body: FFI(ffiFn)->castExpressionToInternalCode, - })->BExpressionT.EValue +let eLambdaFFI = (ffiFn: ffiFn) => { + ffiFn->BModule.eLambdaFFIValue->BExpressionT.EValue } let eNumber = aNumber => aNumber->BInternalExpressionValue.IEvNumber->BExpressionT.EValue @@ -81,6 +74,9 @@ let eBindExpression = (bindingExpr: expression, expression: expression): express let eBindExpressionDefault = (expression: expression): expression => eFunction("$$_bindExpression_$$", list{expression}) +let eTernary = (truth: expression, trueCase: expression, falseCase: expression): expression => + eFunction("$$_ternary_$$", list{truth, trueCase, falseCase}) + let eIdentifier = (name: string): expression => name->BInternalExpressionValue.IEvSymbol->BExpressionT.EValue diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index 28bdbc02..717b4b1d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -3,7 +3,7 @@ module ErrorValue = Reducer_ErrorValue module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T module ExpressionValue = ReducerInterface_InternalExpressionValue -module Module = Reducer_Category_Module +module Module = Reducer_Module module Result = Belt.Result type environment = ReducerInterface_InternalExpressionValue.environment diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res similarity index 87% rename from packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module.res rename to packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res index 584f9089..9f016f66 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Category/Reducer_Category_Module.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res @@ -1,5 +1,6 @@ module ExpressionT = Reducer_Expression_T open ReducerInterface_InternalExpressionValue + let expressionValueToString = toString type t = ReducerInterface_InternalExpressionValue.nameSpace @@ -66,7 +67,7 @@ let set = (nameSpace: t, id: string, value): t => { Belt.Map.String.set(container, id, value)->NameSpace } -let emptyModule: t = NameSpace(Belt.Map.String.empty) +let emptyModule: t = NameSpace(emptyMap) let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings @@ -100,13 +101,25 @@ let removeOther = (nameSpace: t, other: t): t => { })->NameSpace } +external castExpressionToInternalCode: ExpressionT.expressionOrFFI => internalCode = "%identity" +let eLambdaFFIValue = (ffiFn: ExpressionT.ffiFn) => { + IEvLambda({ + parameters: [], + context: emptyModule, + body: FFI(ffiFn)->castExpressionToInternalCode, + }) +} + // -- Module definition let define = (nameSpace: t, identifier: string, ev: internalExpressionValue): t => { let NameSpace(container) = nameSpace - Belt.Map.String.set(container, identifier, ev)->NameSpace // TODO build lambda for polymorphic functions here + Belt.Map.String.set(container, identifier, ev)->NameSpace } let defineNumber = (nameSpace: t, identifier: string, value: float): t => nameSpace->define(identifier, IEvNumber(value)) let defineModule = (nameSpace: t, identifier: string, value: t): t => nameSpace->define(identifier, toExpressionValue(value)) + +let defineFFI = (nameSpace: t, identifier: string, value: ExpressionT.ffiFn): t => + nameSpace->define(identifier, value->eLambdaFFIValue) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res new file mode 100644 index 00000000..d14f5729 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res @@ -0,0 +1,29 @@ +module ExpressionBuilder = Reducer_Expression_ExpressionBuilder +module ExpressionT = Reducer_Expression_T +open Reducer_ErrorValue +open ReducerInterface_InternalExpressionValue + +type expression = ExpressionT.expression + +let defaultCase = (call: functionCall) => + REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error + +let defaultCaseFFIFn = (functionName: string): ExpressionT.ffiFn => { + (args: array, _environment: environment): result< + internalExpressionValue, + errorValue, + > => { + let call = (functionName, args) + defaultCase(call) + } +} + +let defaultCaseFFI = (functionName: string): expression => { + ExpressionBuilder.eLambdaFFI(defaultCaseFFIFn(functionName)) +} + +let addGuard = ( + guard: expression, + expression: expression, + previousExpression: expression, +): expression => ExpressionBuilder.eTernary(guard, expression, previousExpression) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index f7489c26..226d2b6f 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -343,11 +343,18 @@ typeModifierExpression = head:basicType tail:(_ '<-' _nl @typeModifier)* / modifier:identifier _ noArguments { return {modifier: modifier, args: []}; } -basicType = typeConstructor / typeArray / typeRecord / typeInParanthesis / typeIdentifier +basicType = typeConstructor / typeArray / typeTuple / typeRecord / typeInParanthesis / typeIdentifier typeArray = '[' _nl elem:typeExpression _nl ']' {return h.apply('$_typeArray_$', elem)} +typeTuple = '[' _nl elems:array_typeTupleArguments _nl ']' + { return h.apply('$_typeTuple_$', h.constructArray(elems))} + + array_typeTupleArguments + = head:typeExpression tail:(_ ',' _nl @typeExpression)* + { return [head, ...tail]; } + typeRecord = '{' _nl elems:array_typeRecordArguments _nl '}' { return h.apply('$_typeRecord_$', h.constructRecord(elems)); } @@ -375,9 +382,3 @@ typeOfStatement = identifier:identifier _ ':' _nl typeExpression:typeExpression { return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])} typeInParanthesis = '(' _nl typeExpression:typeExpression _nl ')' {return typeExpression} - -// TODO: min max example -// TODO: Example of foo = {a: 2, b: 5}; type fooKeys = string $ memberOf(foo->keys) -// TODO: Example of memberOf( [1,2,3] ) -// TODO: Example of $ -// TODO: Cons(a, list) | EmptyList diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res index 1a37ccc0..67873c61 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res @@ -5,7 +5,11 @@ type node = {"type": string} @module("./Reducer_Peggy_GeneratedParser.js") external parse__: string => node = "parse" -let syntaxErrorToLocation: Js.Exn.t => Reducer_ErrorValue.location = error => %raw(`error.location`) +type withLocation = {"location": Reducer_ErrorValue.location} +external castWithLocation: Js.Exn.t => withLocation = "%identity" + +let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.location => + castWithLocation(error)["location"] @genType let parse = (expr: string): result => diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res new file mode 100644 index 00000000..9b847ca7 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res @@ -0,0 +1,82 @@ +module InternalExpressionValue = ReducerInterface_InternalExpressionValue +open InternalExpressionValue + +type rec iType = + | ItTypeIdentifier(string) + | ItModifiedType({modifiedType: iType}) + | ItTypeOr({typeOr: array}) + | ItTypeFunction({inputs: array, output: iType}) + | ItTypeArray({element: iType}) + | ItTypeTuple({elements: array}) + | ItTypeRecord({properties: Belt.Map.String.t}) + +let rec fromTypeMap = typeMap => { + let default = IEvString("") + let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "typeTag", + default, + ) + let evTypeIdentifier: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "typeIdentifier", + default, + ) + let evTypeOr: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "typeOr", + default, + ) + let evInputs: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "inputs", + default, + ) + let evOutput: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "output", + default, + ) + let evElement: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "element", + default, + ) + let evElements: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "elements", + default, + ) + let evProperties: InternalExpressionValue.t = Belt.Map.String.getWithDefault( + typeMap, + "properties", + default, + ) + //TODO: map type modifiers + switch evTypeTag { + | IEvString("typeIdentifier") => ItModifiedType({modifiedType: fromIEvValue(evTypeIdentifier)}) + | IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)}) + | IEvString("typeFunction") => + ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)}) + | IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)}) + | IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)}) + | IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)}) + | _ => raise(Reducer_Exception.ImpossibleException) + } +} +and fromIEvValue = (ievValue: InternalExpressionValue.t) => + switch ievValue { + | IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier}) + | IEvType(typeMap) => fromTypeMap(typeMap) + | _ => raise(Reducer_Exception.ImpossibleException) + } +and fromIEvArray = (ievArray: InternalExpressionValue.t) => + switch ievArray { + | IEvArray(array) => array->Belt.Array.map(fromIEvValue) + | _ => raise(Reducer_Exception.ImpossibleException) + } +and fromIEvRecord = (ievRecord: InternalExpressionValue.t) => + switch ievRecord { + | IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue) + | _ => raise(Reducer_Exception.ImpossibleException) + } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res new file mode 100644 index 00000000..e51f901a --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res @@ -0,0 +1,88 @@ +open ReducerInterface_InternalExpressionValue + +let typeModifier_memberOf = (aType, anArray) => { + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeIdentifier")), + ("typeIdentifier", aType), + ]) + newRecord->Belt.Map.String.set("memberOf", anArray)->IEvType->Ok +} + +let typeModifier_memberOf_update = (aRecord, anArray) => { + aRecord->Belt.Map.String.set("memberOf", anArray)->IEvType->Ok +} + +let typeModifier_min = (aType, value) => { + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeIdentifier")), + ("typeIdentifier", aType), + ]) + newRecord->Belt.Map.String.set("min", value)->IEvType->Ok +} + +let typeModifier_min_update = (aRecord, value) => { + aRecord->Belt.Map.String.set("min", value)->IEvType->Ok +} + +let typeModifier_max = (aType, value) => { + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeIdentifier")), + ("typeIdentifier", aType), + ]) + newRecord->Belt.Map.String.set("max", value)->IEvType->Ok +} + +let typeModifier_max_update = (aRecord, value) => + aRecord->Belt.Map.String.set("max", value)->IEvType->Ok + +let typeModifier_opaque_update = aRecord => + aRecord->Belt.Map.String.set("opaque", IEvBool(true))->IEvType->Ok + +let typeOr = evArray => { + let newRecord = Belt.Map.String.fromArray([("typeTag", IEvString("typeOr")), ("typeOr", evArray)]) + newRecord->IEvType->Ok +} + +let typeFunction = anArray => { + let output = Belt.Array.getUnsafe(anArray, Js.Array2.length(anArray) - 1) + let inputs = Js.Array2.slice(anArray, ~start=0, ~end_=-1) + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeFunction")), + ("inputs", IEvArray(inputs)), + ("output", output), + ]) + newRecord->IEvType->Ok +} + +let typeArray = element => { + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeTuple")), + ("element", element), + ]) + newRecord->IEvType->Ok +} + +let typeTuple = anArray => { + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeArray")), + ("elements", IEvArray(anArray)), + ]) + newRecord->IEvType->Ok +} + +let typeRecord = arrayOfPairs => { + let newProperties = + Belt.Array.map(arrayOfPairs, pairValue => + switch pairValue { + | IEvArray([IEvString(key), valueValue]) => (key, valueValue) + | _ => ("wrong key type", pairValue->toStringWithType->IEvString) + } + ) + ->Belt.Map.String.fromArray + ->IEvRecord + let newRecord = Belt.Map.String.fromArray([ + ("typeTag", IEvString("typeRecord")), + ("properties", newProperties), + ]) + newRecord->IEvType->Ok +} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res new file mode 100644 index 00000000..b76ba357 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res @@ -0,0 +1,81 @@ +//TODO: Work in progress. Code is commented to make an a release of other features + +module ExpressionT = Reducer_Expression_T +module InternalExpressionValue = ReducerInterface_InternalExpressionValue +module T = Reducer_Type_T +module TypeBuilder = Reducer_Type_TypeBuilder +open InternalExpressionValue + +type typeErrorValue = + | TypeError(T.iType, InternalExpressionValue.t) + | TypeErrorWithPosition(T.iType, InternalExpressionValue.t, int) + | TypeErrorWithProperty(T.iType, InternalExpressionValue.t, string) + +let rec isOfResolvedIType = (anIType: T.iType, aValue): result => { + let caseTypeIdentifier = (anUpperTypeName, aValue) => { + let aTypeName = anUpperTypeName->Js.String2.toLowerCase + let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase + switch aTypeName === valueTypeName { + | true => Ok(true) + | false => TypeError(anIType, aValue)->Error + } + } + + let _caseRecord = (anIType, evValue, propertyMap, map) => { + Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => { + Belt.Result.flatMap(acc, _ => + switch Belt.Map.String.get(map, property) { + | Some(propertyValue) => isOfResolvedIType(propertyType, propertyValue) + | None => TypeErrorWithProperty(anIType, evValue, property)->Error + } + ) + }) + } + let _caseArray = (anIType, evValue, elementType, anArray) => { + Belt.Array.reduceWithIndex(anArray, Ok(true), (acc, element, index) => { + switch isOfResolvedIType(elementType, element) { + | Ok(_) => acc + | Error(_) => TypeErrorWithPosition(anIType, evValue, index)->Error + } + }) + } + + switch anIType { + | ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue) + // | ItModifiedType({modifiedType: anIType}) => raise(Reducer_Exception.ImpossibleException) + // | ItTypeOr({typeOr: anITypeArray}) => raise(Reducer_Exception.ImpossibleException) + // | ItTypeFunction({inputs: anITypeArray, output: anIType}) => + // raise(Reducer_Exception.ImpossibleException) + // | ItTypeArray({element: anIType}) => raise(Reducer_Exception.ImpossibleException) + // | ItTypeTuple({elements: anITypeArray}) => raise(Reducer_Exception.ImpossibleException) + // | ItTypeRecord({properties: anITypeMap}) => raise(Reducer_Exception.ImpossibleException) + | _ => raise(Reducer_Exception.ImpossibleException) + } +} + +let isOfResolvedType = (aType: InternalExpressionValue.t, aValue): result => + aType->T.fromIEvValue->isOfResolvedIType(aValue) + +// let checkArguments = ( +// evFunctionType: InternalExpressionValue.t, +// args: array, +// ) => { +// let functionType = switch evFunctionType { +// | IEvRecord(functionType) => functionType +// | _ => raise(Reducer_Exception.ImpossibleException) +// } +// let evInputs = functionType->Belt.Map.String.getWithDefault("inputs", []->IEvArray) +// let inputs = switch evInputs { +// | IEvArray(inputs) => inputs +// | _ => raise(Reducer_Exception.ImpossibleException) +// } +// let rTupleType = TypeBuilder.typeTuple(inputs) +// Belt.Result.flatMap(rTupleType, tuppleType => isOfResolvedType(tuppleType, args->IEvArray)) +// } + +// let compileTypeExpression = (typeExpression: string, bindings: ExpressionT.bindings, reducerFn: ExpressionT.reducerFn) => { +// statement = `type compiled=${typeExpression}` + +// } + +//TODO: asGuard diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res index bd5d8a35..9bd356d4 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res @@ -24,6 +24,7 @@ type rec externalExpressionValue = | EvDeclaration(lambdaDeclaration) | EvTypeIdentifier(string) | EvModule(record) + | EvType(record) and record = Js.Dict.t and externalBindings = record and lambdaValue = { @@ -50,17 +51,18 @@ let rec toString = aValue => } | EvBool(aBool) => Js.String.make(aBool) | EvCall(fName) => `:${fName}` + | EvDate(date) => DateTime.Date.toString(date) + | EvDeclaration(d) => Declaration.toString(d, r => toString(EvLambda(r))) + | EvDistribution(dist) => GenericDist.toString(dist) | EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)` + | EvModule(m) => `@${m->toStringRecord}` | EvNumber(aNumber) => Js.String.make(aNumber) + | EvRecord(aRecord) => aRecord->toStringRecord | EvString(aString) => `'${aString}'` | EvSymbol(aString) => `:${aString}` - | EvRecord(aRecord) => aRecord->toStringRecord - | EvDistribution(dist) => GenericDist.toString(dist) - | EvDate(date) => DateTime.Date.toString(date) | EvTimeDuration(t) => DateTime.Duration.toString(t) - | EvDeclaration(d) => Declaration.toString(d, r => toString(EvLambda(r))) + | EvType(t) => `type${t->toStringRecord}` | EvTypeIdentifier(id) => `#${id}` - | EvModule(m) => `@${m->toStringRecord}` } and toStringRecord = aRecord => { let pairs = @@ -88,72 +90,3 @@ type environment = DistributionOperation.env @genType let defaultEnvironment: environment = DistributionOperation.defaultEnv - -type expressionValueType = - | EvtArray - | EvtArrayString - | EvtBool - | EvtCall - | EvtDistribution - | EvtLambda - | EvtNumber - | EvtRecord - | EvtString - | EvtSymbol - | EvtDate - | EvtTimeDuration - | EvtDeclaration - | EvtTypeIdentifier - | EvtModule - -type functionCallSignature = CallSignature(string, array) -type functionDefinitionSignature = - FunctionDefinitionSignature(functionCallSignature, expressionValueType) - -let valueToValueType = value => - switch value { - | EvArray(_) => EvtArray - | EvArrayString(_) => EvtArrayString - | EvBool(_) => EvtBool - | EvCall(_) => EvtCall - | EvDistribution(_) => EvtDistribution - | EvLambda(_) => EvtLambda - | EvNumber(_) => EvtNumber - | EvRecord(_) => EvtRecord - | EvString(_) => EvtString - | EvSymbol(_) => EvtSymbol - | EvDate(_) => EvtDate - | EvTimeDuration(_) => EvtTimeDuration - | EvDeclaration(_) => EvtDeclaration - | EvTypeIdentifier(_) => EvtTypeIdentifier - | EvModule(_) => EvtModule - } - -let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => { - let (fn, args) = functionCall - CallSignature(fn, args->Js.Array2.map(valueToValueType)) -} - -let valueTypeToString = (valueType: expressionValueType): string => - switch valueType { - | EvtArray => `Array` - | EvtArrayString => `ArrayString` - | EvtBool => `Bool` - | EvtCall => `Call` - | EvtDistribution => `Distribution` - | EvtLambda => `Lambda` - | EvtNumber => `Number` - | EvtRecord => `Record` - | EvtString => `String` - | EvtSymbol => `Symbol` - | EvtDate => `Date` - | EvtTimeDuration => `Duration` - | EvtDeclaration => `Declaration` - | EvtTypeIdentifier => `TypeIdentifier` - | EvtModule => `Module` - } - -let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => { - let CallSignature(fn, args) = functionCallSignature - `${fn}(${args->Js.Array2.map(valueTypeToString)->Js.Array2.toString})` -} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res index 30de55dd..a6766071 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res @@ -7,8 +7,8 @@ type environment = ExternalExpressionValue.environment let defaultEnvironment = ExternalExpressionValue.defaultEnvironment type rec t = - | IEvArray(array) // FIXME: Convert - | IEvArrayString(array) // FIXME: Convert + | IEvArray(array) // FIXME: Convert to MapInt + | IEvArrayString(array) | IEvBool(bool) | IEvCall(string) // External function call | IEvDate(Js.Date.t) @@ -21,6 +21,7 @@ type rec t = | IEvString(string) | IEvSymbol(string) | IEvTimeDuration(float) + | IEvType(map) | IEvTypeIdentifier(string) and map = Belt.Map.String.t and nameSpace = NameSpace(Belt.Map.String.t) @@ -56,6 +57,7 @@ let rec toString = aValue => | IEvRecord(aMap) => aMap->toStringMap | IEvString(aString) => `'${aString}'` | IEvSymbol(aString) => `:${aString}` + | IEvType(aMap) => aMap->toStringMap | IEvTimeDuration(t) => DateTime.Duration.toString(t) | IEvTypeIdentifier(id) => `#${id}` } @@ -78,17 +80,18 @@ let toStringWithType = aValue => | IEvArrayString(_) => `ArrayString::${toString(aValue)}` | IEvBool(_) => `Bool::${toString(aValue)}` | IEvCall(_) => `Call::${toString(aValue)}` + | IEvDate(_) => `Date::${toString(aValue)}` + | IEvDeclaration(_) => `Declaration::${toString(aValue)}` | IEvDistribution(_) => `Distribution::${toString(aValue)}` | IEvLambda(_) => `Lambda::${toString(aValue)}` + | IEvModule(_) => `Module::${toString(aValue)}` | IEvNumber(_) => `Number::${toString(aValue)}` | IEvRecord(_) => `Record::${toString(aValue)}` | IEvString(_) => `String::${toString(aValue)}` | IEvSymbol(_) => `Symbol::${toString(aValue)}` - | IEvDate(_) => `Date::${toString(aValue)}` | IEvTimeDuration(_) => `Date::${toString(aValue)}` - | IEvDeclaration(_) => `Declaration::${toString(aValue)}` + | IEvType(_) => `Type::${toString(aValue)}` | IEvTypeIdentifier(_) => `TypeIdentifier::${toString(aValue)}` - | IEvModule(_) => `Module::${toString(aValue)}` } let argsToString = (args: array): string => { @@ -120,17 +123,18 @@ type internalExpressionValueType = | EvtArrayString | EvtBool | EvtCall + | EvtDate + | EvtDeclaration | EvtDistribution | EvtLambda + | EvtModule | EvtNumber | EvtRecord | EvtString | EvtSymbol - | EvtDate | EvtTimeDuration - | EvtDeclaration + | EvtType | EvtTypeIdentifier - | EvtModule type functionCallSignature = CallSignature(string, array) type functionDefinitionSignature = @@ -142,17 +146,18 @@ let valueToValueType = value => | IEvArrayString(_) => EvtArrayString | IEvBool(_) => EvtBool | IEvCall(_) => EvtCall + | IEvDate(_) => EvtDate + | IEvDeclaration(_) => EvtDeclaration | IEvDistribution(_) => EvtDistribution | IEvLambda(_) => EvtLambda + | IEvModule(_) => EvtModule | IEvNumber(_) => EvtNumber | IEvRecord(_) => EvtRecord | IEvString(_) => EvtString | IEvSymbol(_) => EvtSymbol - | IEvDate(_) => EvtDate | IEvTimeDuration(_) => EvtTimeDuration - | IEvDeclaration(_) => EvtDeclaration + | IEvType(_) => EvtType | IEvTypeIdentifier(_) => EvtTypeIdentifier - | IEvModule(_) => EvtModule } let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => { @@ -166,17 +171,18 @@ let valueTypeToString = (valueType: internalExpressionValueType): string => | EvtArrayString => `ArrayString` | EvtBool => `Bool` | EvtCall => `Call` + | EvtDate => `Date` + | EvtDeclaration => `Declaration` | EvtDistribution => `Distribution` | EvtLambda => `Lambda` + | EvtModule => `Module` | EvtNumber => `Number` | EvtRecord => `Record` | EvtString => `String` | EvtSymbol => `Symbol` - | EvtDate => `Date` | EvtTimeDuration => `Duration` - | EvtDeclaration => `Declaration` + | EvtType => `Type` | EvtTypeIdentifier => `TypeIdentifier` - | EvtModule => `Module` } let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => { @@ -190,6 +196,11 @@ let rec toExternal = (iev: t): ExternalExpressionValue.t => { | IEvArrayString(v) => EvArrayString(v) | IEvBool(v) => EvBool(v) | IEvCall(v) => EvCall(v) + | IEvDeclaration(v) => { + let fn = lambdaValueToExternal(v.fn) + let args = v.args + EvDeclaration({fn: fn, args: args}) + } | IEvDistribution(v) => EvDistribution(v) | IEvLambda(v) => EvLambda(lambdaValueToExternal(v)) | IEvNumber(v) => EvNumber(v) @@ -198,11 +209,7 @@ let rec toExternal = (iev: t): ExternalExpressionValue.t => { | IEvSymbol(v) => EvSymbol(v) | IEvDate(v) => EvDate(v) | IEvTimeDuration(v) => EvTimeDuration(v) - | IEvDeclaration(v) => { - let fn = lambdaValueToExternal(v.fn) - let args = v.args - EvDeclaration({fn: fn, args: args}) - } + | IEvType(v) => v->mapToExternal->EvType | IEvTypeIdentifier(v) => EvTypeIdentifier(v) | IEvModule(v) => v->nameSpaceToTypeScriptBindings->EvModule } @@ -228,21 +235,22 @@ let rec toInternal = (ev: ExternalExpressionValue.t): t => { | EvArrayString(v) => IEvArrayString(v) | EvBool(v) => IEvBool(v) | EvCall(v) => IEvCall(v) - | EvDistribution(v) => IEvDistribution(v) - | EvLambda(v) => IEvLambda(lambdaValueToInternal(v)) - | EvNumber(v) => IEvNumber(v) - | EvRecord(v) => v->recordToInternal->IEvRecord - | EvString(v) => IEvString(v) - | EvSymbol(v) => IEvSymbol(v) | EvDate(v) => IEvDate(v) - | EvTimeDuration(v) => IEvTimeDuration(v) | EvDeclaration(v) => { let fn = lambdaValueToInternal(v.fn) let args = v.args IEvDeclaration({fn: fn, args: args}) } - | EvTypeIdentifier(v) => IEvTypeIdentifier(v) + | EvDistribution(v) => IEvDistribution(v) + | EvLambda(v) => IEvLambda(lambdaValueToInternal(v)) | EvModule(v) => v->nameSpaceFromTypeScriptBindings->IEvModule + | EvNumber(v) => IEvNumber(v) + | EvRecord(v) => v->recordToInternal->IEvRecord + | EvString(v) => IEvString(v) + | EvSymbol(v) => IEvSymbol(v) + | EvTimeDuration(v) => IEvTimeDuration(v) + | EvType(v) => v->recordToInternal->IEvType + | EvTypeIdentifier(v) => IEvTypeIdentifier(v) } } and recordToInternal = v => diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res index b714790a..c08b7cb1 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res @@ -1,4 +1,4 @@ -module Module = Reducer_Category_Module +module Module = Reducer_Module let internalStdLib = Module.emptyModule->SquiggleLibrary_Math.makeBindings diff --git a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res index ba049bea..d85927a1 100644 --- a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res +++ b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res @@ -1,5 +1,5 @@ -module Bindings = Reducer_Category_Module -module Module = Reducer_Category_Module +module Bindings = Reducer_Module +module Module = Reducer_Module let availableNumbers: array<(string, float)> = [ ("pi", Js.Math._PI), From d888f685d0edde81bf808b5714b144c2b7585bad Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Jul 2022 14:25:05 -0400 Subject: [PATCH 10/29] Quick fixes for EvType typescripting --- packages/squiggle-lang/src/js/index.ts | 8 ++++++++ packages/squiggle-lang/src/js/rescript_interop.ts | 1 + 2 files changed, 9 insertions(+) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 6d847307..a1a97add 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -172,6 +172,14 @@ function createTsExport( 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", diff --git a/packages/squiggle-lang/src/js/rescript_interop.ts b/packages/squiggle-lang/src/js/rescript_interop.ts index 41baa2e3..dcba24e6 100644 --- a/packages/squiggle-lang/src/js/rescript_interop.ts +++ b/packages/squiggle-lang/src/js/rescript_interop.ts @@ -129,6 +129,7 @@ export type squiggleExpression = | tagged<"timeDuration", number> | tagged<"lambdaDeclaration", lambdaDeclaration> | tagged<"record", { [key: string]: squiggleExpression }> + | tagged<"type", { [key: string]: squiggleExpression }> | tagged<"typeIdentifier", string> | tagged<"module", { [key: string]: squiggleExpression }>; From d6bdf5bfebec4806de35334d3c66d9c8bbcfdd29 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Sat, 2 Jul 2022 15:02:43 -0400 Subject: [PATCH 11/29] Formatted code --- packages/squiggle-lang/src/js/index.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index a1a97add..5e4cf2c1 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -173,13 +173,14 @@ function createTsExport( 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 + 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", From 7a419742ad5f91afd07ff6a7deb828ad8c50632a Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 4 Jul 2022 09:57:00 -0400 Subject: [PATCH 12/29] moved error message sooner for invalid mean and stdev --- .../Distributions/SymbolicDist/SymbolicDist.res | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res index 0b192b5f..e7471ffa 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SymbolicDist/SymbolicDist.res @@ -129,14 +129,18 @@ module Beta = { // https://en.wikipedia.org/wiki/Beta_distribution#Mean_and_sample_size let alpha = mean *. sampleSize let beta = (1.0 -. mean) *. sampleSize - alpha->make(beta) + make(alpha, beta) } let fromMeanAndStdev = (mean, stdev) => { // https://en.wikipedia.org/wiki/Beta_distribution#Mean_and_variance - let var = stdev *. stdev - let sampleSize = mean *. (1.0 -. mean) /. var -. 1.0 - mean->fromMeanAndSampleSize(sampleSize) + if !(0.0 < stdev && stdev <= 0.5) || !(0.0 <= mean && mean <= 1.0) { + "Beta mean must be in [0,1] and stdev must be in (1,0.5]"->Error + } else { + let var = stdev *. stdev + let sampleSize = mean *. (1.0 -. mean) /. var -. 1.0 + fromMeanAndSampleSize(mean, sampleSize) + } } } From c0329dc73d231cefc26797e50d9e5c868efe3eb9 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 00:57:11 +0200 Subject: [PATCH 13/29] resolve PR786 --- .../Reducer/Reducer_Type/Reducer_Type_TypeChecker.res | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res index b76ba357..18cb804a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res @@ -1,5 +1,3 @@ -//TODO: Work in progress. Code is commented to make an a release of other features - module ExpressionT = Reducer_Expression_T module InternalExpressionValue = ReducerInterface_InternalExpressionValue module T = Reducer_Type_T @@ -42,6 +40,7 @@ let rec isOfResolvedIType = (anIType: T.iType, aValue): result caseTypeIdentifier(name, aValue) + // TODO: Work in progress. Code is commented to make an a release of other features // | ItModifiedType({modifiedType: anIType}) => raise(Reducer_Exception.ImpossibleException) // | ItTypeOr({typeOr: anITypeArray}) => raise(Reducer_Exception.ImpossibleException) // | ItTypeFunction({inputs: anITypeArray, output: anIType}) => @@ -56,6 +55,7 @@ let rec isOfResolvedIType = (anIType: T.iType, aValue): result => aType->T.fromIEvValue->isOfResolvedIType(aValue) +// TODO: Work in progress. Code is commented to make an a release of other features // let checkArguments = ( // evFunctionType: InternalExpressionValue.t, // args: array, From 6edacc78e465f38f6b29cc5f4e96f276b50c808a Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 04:30:08 +0200 Subject: [PATCH 14/29] foo test --- .../Reducer_Module_defineFFI_test.res | 94 +++++++++++++++++++ .../Reducer_Expression_Lambda.res | 19 ++-- .../Reducer_Expression_T.res | 2 + .../Reducer/Reducer_Module/Reducer_Module.res | 41 +++++++- .../Reducer_Module/Reducer_Module_Lambda.res | 18 +--- 5 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res new file mode 100644 index 00000000..1cc238a9 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res @@ -0,0 +1,94 @@ +module InternalExpressionValue = ReducerInterface_InternalExpressionValue +module ExpressionT = Reducer_Expression_T +module Module = Reducer_Module +module Bindings = Reducer_Module +module ErrorValue = Reducer_ErrorValue + +open Jest +open Expect + +// ---------------------- +// --- Start of Module File +// ---------------------- + +module FooImplementation = { + let fooNumber = 0.0 + let fooString = "Foo String" + let fooBool = true + let makeFoo = (a: string, b: string, _environment): string => `I am ${a}-foo and I am ${b}-foo` + let makeBar = (a: float, b: float, _environment): string => + `I am ${a->Js.Float.toString}-bar and I am ${b->Js.Float.toString}-bar` +} + +module FooFFI = { + let makeFoo: ExpressionT.optionFfiFn = (args: array, environment) => { + switch args { + | [IEvString(a), IEvString(b)] => FooImplementation.makeFoo(a, b, environment)->IEvString->Some + | _ => None + } + } + let makeBar: ExpressionT.optionFfiFn = (args: array, environment) => + switch args { + | [IEvNumber(a), IEvNumber(b)] => FooImplementation.makeBar(a, b, environment)->IEvString->Some + | _ => None + } +} + +let fooModule: Module.t = + Module.emptyStdLib + ->Module.defineNumber("fooNumber", FooImplementation.fooNumber) + ->Module.defineString("fooString", FooImplementation.fooString) + ->Module.defineBool("fooBool", FooImplementation.fooBool) + ->Module.defineFunction("makeFoo", FooFFI.makeFoo) + ->Module.defineFunction("makeBar", FooFFI.makeBar) + +let makeBindings = (prevBindings: Bindings.t): Bindings.t => + prevBindings->Module.defineModule("Foo", fooModule) + +// ---------------------- +// --- End of Module File +// ---------------------- + +let stdLibWithFoo = Bindings.emptyBindings->makeBindings +let evalWithFoo = sourceCode => + Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr => + Reducer_Expression.reduceExpression( + expr, + stdLibWithFoo, + InternalExpressionValue.defaultEnvironment, + ) + ) +let evalToStringResultWithFoo = sourceCode => + evalWithFoo(sourceCode)->InternalExpressionValue.toStringResult + +describe("Module", () => { + test("fooNumber", () => { + let result = evalToStringResultWithFoo("Foo.fooNumber") + expect(result)->toEqual("Ok(0)") + }) + test("fooString", () => { + let result = evalToStringResultWithFoo("Foo.fooString") + expect(result)->toEqual("Ok('Foo String')") + }) + test("fooBool", () => { + let result = evalToStringResultWithFoo("Foo.fooBool") + expect(result)->toEqual("Ok(true)") + }) + test("fooBool", () => { + let result = evalToStringResultWithFoo("Foo.fooBool") + expect(result)->toEqual("Ok(true)") + }) + test("makeFoo", () => { + let result = evalToStringResultWithFoo("Foo.makeFoo('a', 'b')") + expect(result)->toEqual("Ok('I am a-foo and I am b-foo')") + }) + test("makeFoo wrong arguments", () => { + let result = evalToStringResultWithFoo("Foo.makeFoo(1, 2)") + // Notice the error with types + expect(result)->toEqual("Error(Function not found: makeFoo(Number,Number))") + }) + test("makeBar", () => { + let result = evalToStringResultWithFoo("Foo.makeBar(1, 2)") + expect(result)->toEqual("Ok('I am 1-bar and I am 2-bar')") + }) +}) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index 717b4b1d..bf52b398 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -19,12 +19,19 @@ let checkArity = ( lambdaValue: ExpressionValue.lambdaValue, args: list, ) => { - let argsLength = Belt.List.length(args) - let parametersLength = Js.Array2.length(lambdaValue.parameters) - if argsLength !== parametersLength { - ErrorValue.REArityError(None, parametersLength, argsLength)->Error - } else { - args->Ok + let reallyCheck = { + let argsLength = Belt.List.length(args) + let parametersLength = Js.Array2.length(lambdaValue.parameters) + if argsLength !== parametersLength { + ErrorValue.REArityError(None, parametersLength, argsLength)->Error + } else { + args->Ok + } + } + let exprOrFFI = castInternalCodeToExpression(lambdaValue.body) + switch exprOrFFI { + | NotFFI(_) => reallyCheck + | FFI(_) => args->Ok } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res index 38910da2..dee70c88 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res @@ -71,6 +71,8 @@ type ffiFn = ( environment, ) => result +type optionFfiFn = (array, environment) => option + type expressionOrFFI = | NotFFI(expression) | FFI(ffiFn) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res index 9f016f66..62f9d8c7 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module.res @@ -1,4 +1,6 @@ module ExpressionT = Reducer_Expression_T +module InternalExpressionValue = ReducerInterface_InternalExpressionValue +open Reducer_ErrorValue open ReducerInterface_InternalExpressionValue let expressionValueToString = toString @@ -68,6 +70,7 @@ let set = (nameSpace: t, id: string, value): t => { } let emptyModule: t = NameSpace(emptyMap) +let emptyBindings = emptyModule let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings @@ -110,16 +113,50 @@ let eLambdaFFIValue = (ffiFn: ExpressionT.ffiFn) => { }) } +let functionNotFoundError = (call: functionCall) => + REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error + +let functionNotFoundErrorFFIFn = (functionName: string): ExpressionT.ffiFn => { + (args: array, _environment: environment): result< + internalExpressionValue, + errorValue, + > => { + let call = (functionName, args) + functionNotFoundError(call) + } +} + +let convertOptionToFfiFn = ( + myFunctionName: string, + myFunction: ExpressionT.optionFfiFn, +): ExpressionT.ffiFn => { + (args: array, environment) => { + myFunction(args, environment) + ->Belt.Option.map(v => v->Ok) + ->Belt.Option.getWithDefault(functionNotFoundErrorFFIFn(myFunctionName)(args, environment)) + } +} + // -- Module definition let define = (nameSpace: t, identifier: string, ev: internalExpressionValue): t => { let NameSpace(container) = nameSpace Belt.Map.String.set(container, identifier, ev)->NameSpace } + let defineNumber = (nameSpace: t, identifier: string, value: float): t => nameSpace->define(identifier, IEvNumber(value)) +let defineString = (nameSpace: t, identifier: string, value: string): t => + nameSpace->define(identifier, IEvString(value)) + +let defineBool = (nameSpace: t, identifier: string, value: bool): t => + nameSpace->define(identifier, IEvBool(value)) + let defineModule = (nameSpace: t, identifier: string, value: t): t => nameSpace->define(identifier, toExpressionValue(value)) -let defineFFI = (nameSpace: t, identifier: string, value: ExpressionT.ffiFn): t => - nameSpace->define(identifier, value->eLambdaFFIValue) +let defineFunction = (nameSpace: t, identifier: string, value: ExpressionT.optionFfiFn): t => { + nameSpace->define(identifier, convertOptionToFfiFn(identifier, value)->eLambdaFFIValue) +} + +let emptyStdLib: t = emptyModule->defineBool("stdlib", true) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res index d14f5729..7c3c5e54 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res @@ -1,25 +1,11 @@ module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T -open Reducer_ErrorValue -open ReducerInterface_InternalExpressionValue +// open ReducerInterface_InternalExpressionValue type expression = ExpressionT.expression -let defaultCase = (call: functionCall) => - REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error - -let defaultCaseFFIFn = (functionName: string): ExpressionT.ffiFn => { - (args: array, _environment: environment): result< - internalExpressionValue, - errorValue, - > => { - let call = (functionName, args) - defaultCase(call) - } -} - let defaultCaseFFI = (functionName: string): expression => { - ExpressionBuilder.eLambdaFFI(defaultCaseFFIFn(functionName)) + ExpressionBuilder.eLambdaFFI(Reducer_Module.functionNotFoundErrorFFIFn(functionName)) } let addGuard = ( From 742fc10e0d8c266e970955780ed26d09693a087e Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 05:54:56 +0200 Subject: [PATCH 15/29] comment --- .../Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res index 1cc238a9..7b6a5612 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res @@ -12,7 +12,10 @@ open Expect // ---------------------- module FooImplementation = { - let fooNumber = 0.0 + // As this is a Rescript module, functions can use other functions in this module + // and in other stdLib modules implemented this way. + // Embedding function definitions in to switch statements is a bad practice + // - to reduce line count or to let fooNumber = 0.0 let fooString = "Foo String" let fooBool = true let makeFoo = (a: string, b: string, _environment): string => `I am ${a}-foo and I am ${b}-foo` From a285d0b493a0057545b20cf536e2b59a0ea5a97a Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 06:17:03 +0200 Subject: [PATCH 16/29] fix peggy warning --- .../Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index 226d2b6f..e2b5234e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -154,9 +154,7 @@ unary unaryOperator "unary operator" = ('-' / '.-' / '!' ) -postOperator = indexedValue - -indexedValue +postOperator = collectionElement / atom From f5f2d94fbc198a2d008f376e1b8b1570384842b7 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 06:17:15 +0200 Subject: [PATCH 17/29] remove dead line --- .../Reducer/Reducer_Expression/Reducer_Expression_Lambda.res | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index bf52b398..fabae269 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -10,7 +10,6 @@ type environment = ReducerInterface_InternalExpressionValue.environment type expression = ExpressionT.expression type expressionOrFFI = ExpressionT.expressionOrFFI type internalExpressionValue = ReducerInterface_InternalExpressionValue.t -// type tmpExternalBindings = ReducerInterface_InternalExpressionValue.tmpExternalBindings type internalCode = ReducerInterface_InternalExpressionValue.internalCode external castInternalCodeToExpression: internalCode => expressionOrFFI = "%identity" From 746ca695afe2eb9aec5f9961283004c30c192ce2 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 06:17:26 +0200 Subject: [PATCH 18/29] fix test --- .../Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res index 7b6a5612..9573654f 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res @@ -15,7 +15,8 @@ module FooImplementation = { // As this is a Rescript module, functions can use other functions in this module // and in other stdLib modules implemented this way. // Embedding function definitions in to switch statements is a bad practice - // - to reduce line count or to let fooNumber = 0.0 + // - to reduce line count or to + let fooNumber = 0.0 let fooString = "Foo String" let fooBool = true let makeFoo = (a: string, b: string, _environment): string => `I am ${a}-foo and I am ${b}-foo` From 29c9cad510454a3afa5cc4d229101bcba362e30c Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 06:19:42 +0200 Subject: [PATCH 19/29] remove dead line --- .../rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res index 7c3c5e54..922b5c6e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Module/Reducer_Module_Lambda.res @@ -1,6 +1,5 @@ module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T -// open ReducerInterface_InternalExpressionValue type expression = ExpressionT.expression From e0b2632028cd1ddb318a0e7e48bc9385e5c14be8 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 06:20:57 +0200 Subject: [PATCH 20/29] format --- .../Reducer_Module/Reducer_Module_defineFFI_test.res | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res index 9573654f..fa3f13fa 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res @@ -12,10 +12,10 @@ open Expect // ---------------------- module FooImplementation = { - // As this is a Rescript module, functions can use other functions in this module - // and in other stdLib modules implemented this way. - // Embedding function definitions in to switch statements is a bad practice - // - to reduce line count or to + // As this is a Rescript module, functions can use other functions in this module + // and in other stdLib modules implemented this way. + // Embedding function definitions in to switch statements is a bad practice + // - to reduce line count or to let fooNumber = 0.0 let fooString = "Foo String" let fooBool = true From c4d2ad922a3df2dbaf87675124fedd4e95721567 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 5 Jul 2022 17:32:02 +1000 Subject: [PATCH 21/29] Respond to PR comments --- packages/components/README.md | 4 +- .../src/components/SquiggleEditor.tsx | 41 ++++++++----------- .../src/components/SquigglePlayground.tsx | 19 ++++----- packages/components/src/lib/hooks.ts | 22 +++++++++- .../src/stories/SquiggleEditor.stories.mdx | 13 ++++++ .../stories/SquigglePlayground.stories.mdx | 2 +- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/packages/components/README.md b/packages/components/README.md index bc8d3123..63a98d34 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -20,7 +20,7 @@ Add to `App.js`: ```jsx import { SquiggleEditor } from "@quri/squiggle-components"; ; ``` @@ -50,7 +50,7 @@ export function DynamicSquiggleChart({ squiggleString }) { } else { return ( = (props) => { - let defaultCode = props.defaultCode ?? ""; - const [uncontrolledCode, setCode] = useState(defaultCode); - let code = props.code ?? uncontrolledCode; + const [code, setCode] = useMaybeControlledValue({ + value: props.code, + defaultValue: props.defaultCode, + onChange: props.onCodeChange, + }); let chartProps = { ...props, code }; return ( - { - if (props.onCodeChange) props.onCodeChange(code); - - if (props.code === undefined) setCode(code); - }} - /> + ); @@ -66,7 +61,7 @@ export interface SquigglePartialProps { } export const SquigglePartial: React.FC = ({ - code, + code: controlledCode, defaultCode = "", onChange, onCodeChange, @@ -74,11 +69,14 @@ export const SquigglePartial: React.FC = ({ environment, jsImports = defaultImports, }: SquigglePartialProps) => { - const [uncontrolledCode, setCode] = useState(defaultCode); - let codeProp = code ?? uncontrolledCode; + const [code, setCode] = useMaybeControlledValue({ + value: controlledCode, + defaultValue: defaultCode, + onChange: onCodeChange, + }); const result = useSquigglePartial({ - code: codeProp, + code, bindings, environment, jsImports, @@ -87,14 +85,7 @@ export const SquigglePartial: React.FC = ({ return ( - { - if (onCodeChange) onCodeChange(code); - - if (code === undefined) setCode(code); - }} - /> + {result.tag !== "Ok" ? : null} ); diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index e96a1053..4b01a344 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -1,6 +1,7 @@ import React, { FC, Fragment, useState, useEffect } from "react"; import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form"; import * as yup from "yup"; +import { useMaybeControlledValue } from "../lib/hooks"; import { yupResolver } from "@hookform/resolvers/yup"; import { Tab } from "@headlessui/react"; import { @@ -216,7 +217,11 @@ export const SquigglePlayground: FC = ({ onSettingsChange, showEditor = true, }) => { - const [uncontrolledCode, setUncontrolledCode] = useState(defaultCode); + const [code, setCode] = useMaybeControlledValue({ + value: controlledCode, + defaultValue: defaultCode, + onChange: onCodeChange, + }); const [importString, setImportString] = useState("{}"); const [imports, setImports] = useState({}); const [importsAreValid, setImportsAreValid] = useState(true); @@ -261,8 +266,6 @@ export const SquigglePlayground: FC = ({ } }; - const code = controlledCode ?? uncontrolledCode; - const samplingSettings = (
@@ -434,14 +437,8 @@ export const SquigglePlayground: FC = ({ const firstTab = vars.showEditor ? (
{ - if (controlledCode === undefined) { - // uncontrolled mode - setUncontrolledCode(newCode); - } - onCodeChange?.(newCode); - }} + value={code ?? ""} + onChange={setCode} oneLine={false} showGutter={true} height={height - 1} diff --git a/packages/components/src/lib/hooks.ts b/packages/components/src/lib/hooks.ts index 03b8c69d..b52c23be 100644 --- a/packages/components/src/lib/hooks.ts +++ b/packages/components/src/lib/hooks.ts @@ -5,7 +5,7 @@ import { run, runPartial, } from "@quri/squiggle-lang"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; type SquiggleArgs> = { code: string; @@ -42,3 +42,23 @@ export const useSquigglePartial = ( export const useSquiggle = (args: SquiggleArgs>) => { return useSquiggleAny(args, run); }; + +type ControlledValueArgs = { + value?: T; + defaultValue: T; + onChange?: (x: T) => void; +}; +export function useMaybeControlledValue( + args: ControlledValueArgs +): [T, (x: T) => void] { + let [uncontrolledValue, setUncontrolledValue] = useState(args.defaultValue); + let value = args.value ?? uncontrolledValue; + let onChange = (newValue: T) => { + if (args.value === undefined) { + // uncontrolled mode + setUncontrolledValue(newValue); + } + args.onChange?.(newValue); + }; + return [value, onChange]; +} diff --git a/packages/components/src/stories/SquiggleEditor.stories.mdx b/packages/components/src/stories/SquiggleEditor.stories.mdx index e51e3e33..086614c6 100644 --- a/packages/components/src/stories/SquiggleEditor.stories.mdx +++ b/packages/components/src/stories/SquiggleEditor.stories.mdx @@ -21,6 +21,19 @@ the distribution. +It's also possible to create a controlled version of the same component + + + + {Template.bind({})} + + + You can also name variables like so: diff --git a/packages/components/src/stories/SquigglePlayground.stories.mdx b/packages/components/src/stories/SquigglePlayground.stories.mdx index eb39bbba..0c198f42 100644 --- a/packages/components/src/stories/SquigglePlayground.stories.mdx +++ b/packages/components/src/stories/SquigglePlayground.stories.mdx @@ -14,7 +14,7 @@ including sampling settings, in squiggle. From 2c2f299e466e889d81f952b0b4ad7f6112448cd5 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Tue, 5 Jul 2022 17:43:13 +1000 Subject: [PATCH 22/29] Fix build error --- packages/components/src/components/SquiggleEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 9ef2bea7..027eecb9 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -30,7 +30,7 @@ export type SquiggleEditorProps = SquiggleChartProps & { export const SquiggleEditor: React.FC = (props) => { const [code, setCode] = useMaybeControlledValue({ value: props.code, - defaultValue: props.defaultCode, + defaultValue: props.defaultCode ?? "", onChange: props.onCodeChange, }); From b3ac8848d2ac8bfc6a7061a7ce1db299d66fa0ac Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Tue, 5 Jul 2022 15:27:56 +0200 Subject: [PATCH 23/29] comment --- .../Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res index fa3f13fa..a06c7a57 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Module/Reducer_Module_defineFFI_test.res @@ -24,6 +24,8 @@ module FooImplementation = { `I am ${a->Js.Float.toString}-bar and I am ${b->Js.Float.toString}-bar` } +// There is a potential for type modules to define lift functions +// for their own type to get rid of switch statements. module FooFFI = { let makeFoo: ExpressionT.optionFfiFn = (args: array, environment) => { switch args { From 9208330038470997328a4c6b3363eb487bf496d5 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 26 Jun 2022 21:15:09 +0300 Subject: [PATCH 24/29] playground: manual play mode --- .../src/components/SquiggleChart.tsx | 104 +++++++++-------- .../src/components/SquigglePlayground.tsx | 109 +++++++++++++++--- .../components/src/components/ui/Toggle.tsx | 34 ++++++ 3 files changed, 180 insertions(+), 67 deletions(-) create mode 100644 packages/components/src/components/ui/Toggle.tsx diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 9e352a62..bfb69a4e 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -48,57 +48,59 @@ export interface SquiggleChartProps { const defaultOnChange = () => {}; -export const SquiggleChart: React.FC = ({ - code = "", - environment, - onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here - height = 200, - bindings = defaultBindings, - jsImports = defaultImports, - showSummary = false, - width, - showTypes = false, - showControls = false, - logX = false, - expY = false, - diagramStart = 0, - diagramStop = 10, - diagramCount = 100, -}) => { - const result = useSquiggle({ - code, - bindings, +export const SquiggleChart: React.FC = React.memo( + ({ + code = "", environment, - jsImports, - onChange, - }); + onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here + height = 200, + bindings = defaultBindings, + jsImports = defaultImports, + showSummary = false, + width, + showTypes = false, + showControls = false, + logX = false, + expY = false, + diagramStart = 0, + diagramStop = 10, + diagramCount = 100, + }) => { + const result = useSquiggle({ + code, + bindings, + environment, + jsImports, + onChange, + }); - if (result.tag !== "Ok") { - return ; + if (result.tag !== "Ok") { + return ; + } + + let distributionPlotSettings = { + showControls, + showSummary, + logX, + expY, + }; + + let chartSettings = { + start: diagramStart, + stop: diagramStop, + count: diagramCount, + }; + + return ( + + ); } - - let distributionPlotSettings = { - showControls, - showSummary, - logX, - expY, - }; - - let chartSettings = { - start: diagramStart, - stop: diagramStop, - count: diagramCount, - }; - - return ( - - ); -}; +); diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 4b01a344..74f48455 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -1,4 +1,4 @@ -import React, { FC, Fragment, useState, useEffect } from "react"; +import React, { FC, Fragment, useState, useEffect, useMemo } from "react"; import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form"; import * as yup from "yup"; import { useMaybeControlledValue } from "../lib/hooks"; @@ -6,10 +6,14 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { Tab } from "@headlessui/react"; import { ChartSquareBarIcon, + CheckCircleIcon, CodeIcon, CogIcon, CurrencyDollarIcon, EyeIcon, + PauseIcon, + PlayIcon, + RefreshIcon, } from "@heroicons/react/solid"; import clsx from "clsx"; @@ -20,6 +24,7 @@ import { CodeEditor } from "./CodeEditor"; import { JsonEditor } from "./JsonEditor"; import { ErrorAlert, SuccessAlert } from "./Alert"; import { SquiggleContainer } from "./SquiggleContainer"; +import { Toggle } from "./ui/Toggle"; interface PlaygroundProps { /** The initial squiggle string to put in the playground */ @@ -110,7 +115,7 @@ type StyledTabProps = { const StyledTab: React.FC = ({ name, icon: Icon }) => { return ( - + {({ selected }) => ( + )} + +
+ ); +}; + export const SquigglePlayground: FC = ({ defaultCode = "", height = 500, @@ -225,7 +277,14 @@ export const SquigglePlayground: FC = ({ const [importString, setImportString] = useState("{}"); const [imports, setImports] = useState({}); const [importsAreValid, setImportsAreValid] = useState(true); - const { register, control } = useForm({ + + const [renderedCode, setRenderedCode] = useState(""); // used only if autoplay is false + + const { + register, + control, + setValue: setFormValue, + } = useForm({ resolver: yupResolver(schema), defaultValues: { sampleCount: 1000, @@ -242,6 +301,7 @@ export const SquigglePlayground: FC = ({ diagramStart: 0, diagramStop: 10, diagramCount: 20, + autoplay: true, }, }); const vars = useWatch({ @@ -252,10 +312,14 @@ export const SquigglePlayground: FC = ({ onSettingsChange?.(vars); }, [vars, onSettingsChange]); - const env: environment = { - sampleCount: Number(vars.sampleCount), - xyPointLength: Number(vars.xyPointLength), - }; + const env: environment = useMemo( + () => ({ + sampleCount: Number(vars.sampleCount), + xyPointLength: Number(vars.xyPointLength), + }), + [vars.sampleCount, vars.xyPointLength] + ); + const getChangeJson = (r: string) => { setImportString(r); try { @@ -418,7 +482,7 @@ export const SquigglePlayground: FC = ({ const squiggleChart = ( = ({
- - + + + + + + + { + setRenderedCode(code); + }} + onAutoplayChange={(newValue) => { + if (!newValue) setRenderedCode(code); + setFormValue("autoplay", newValue); + }} /> - - - - +
{vars.showEditor ? withEditor : withoutEditor}
diff --git a/packages/components/src/components/ui/Toggle.tsx b/packages/components/src/components/ui/Toggle.tsx new file mode 100644 index 00000000..1b96db77 --- /dev/null +++ b/packages/components/src/components/ui/Toggle.tsx @@ -0,0 +1,34 @@ +import clsx from "clsx"; +import React from "react"; + +type IconType = (props: React.ComponentProps<"svg">) => JSX.Element; + +type Props = { + status: boolean; + onChange: (status: boolean) => void; + texts: [string, string]; + icons: [IconType, IconType]; +}; + +export const Toggle: React.FC = ({ + texts: [onText, offText], + icons: [OnIcon, OffIcon], + status, + onChange, +}) => { + const CurrentIcon = status ? OnIcon : OffIcon; + return ( + + ); +}; From 4fe30107a4f953f3be4b8d8f8f266af8b3996365 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 26 Jun 2022 21:30:24 +0300 Subject: [PATCH 25/29] submit editor on cmd+enter --- packages/components/src/components/CodeEditor.tsx | 15 ++++++++++++++- .../src/components/SquigglePlayground.tsx | 10 +++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/CodeEditor.tsx b/packages/components/src/components/CodeEditor.tsx index c869b2ea..15802131 100644 --- a/packages/components/src/components/CodeEditor.tsx +++ b/packages/components/src/components/CodeEditor.tsx @@ -1,5 +1,5 @@ import _ from "lodash"; -import React, { FC, useMemo } from "react"; +import React, { FC, useMemo, useRef } from "react"; import AceEditor from "react-ace"; import "ace-builds/src-noconflict/mode-golang"; @@ -8,6 +8,7 @@ import "ace-builds/src-noconflict/theme-github"; interface CodeEditorProps { value: string; onChange: (value: string) => void; + onSubmit?: () => void; oneLine?: boolean; width?: number; height: number; @@ -17,6 +18,7 @@ interface CodeEditorProps { export const CodeEditor: FC = ({ value, onChange, + onSubmit, oneLine = false, showGutter = false, height, @@ -24,6 +26,10 @@ export const CodeEditor: FC = ({ const lineCount = value.split("\n").length; const id = useMemo(() => _.uniqueId(), []); + // this is necessary because AceEditor binds commands on mount, see https://github.com/securingsincity/react-ace/issues/684 + const onSubmitRef = useRef(null); + onSubmitRef.current = onSubmit; + return ( = ({ enableBasicAutocompletion: false, enableLiveAutocompletion: false, }} + commands={[ + { + name: "submit", + bindKey: { mac: "Cmd-Enter", win: "Ctrl-Enter" }, + exec: () => onSubmitRef.current?.(), + }, + ]} /> ); }; diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 74f48455..06b3fd3d 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -330,6 +330,11 @@ export const SquigglePlayground: FC = ({ } }; + const manualPlay = () => { + if (vars.autoplay) return; // should we allow reruns even in autoplay mode? + setRenderedCode(code); // TODO - force play even if code hasn't changed + }; + const samplingSettings = (
@@ -503,6 +508,7 @@ export const SquigglePlayground: FC = ({ = ({ { - setRenderedCode(code); - }} + onPlay={manualPlay} onAutoplayChange={(newValue) => { if (!newValue) setRenderedCode(code); setFormValue("autoplay", newValue); From 87bb752c925fd00367251ae1096897e4cd1ea682 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 26 Jun 2022 23:30:32 +0300 Subject: [PATCH 26/29] extract ui components from playground; show spinner on keyboard-initiated plays --- .../src/components/SquigglePlayground.tsx | 576 ++++++++---------- .../components/src/components/ui/Checkbox.tsx | 24 + .../src/components/ui/StyledTab.tsx | 60 ++ 3 files changed, 349 insertions(+), 311 deletions(-) create mode 100644 packages/components/src/components/ui/Checkbox.tsx create mode 100644 packages/components/src/components/ui/StyledTab.tsx diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 06b3fd3d..c5104508 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -1,9 +1,8 @@ -import React, { FC, Fragment, useState, useEffect, useMemo } from "react"; +import React, { FC, useState, useEffect, useMemo } from "react"; import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form"; import * as yup from "yup"; import { useMaybeControlledValue } from "../lib/hooks"; import { yupResolver } from "@hookform/resolvers/yup"; -import { Tab } from "@headlessui/react"; import { ChartSquareBarIcon, CheckCircleIcon, @@ -25,6 +24,8 @@ import { JsonEditor } from "./JsonEditor"; import { ErrorAlert, SuccessAlert } from "./Alert"; import { SquiggleContainer } from "./SquiggleContainer"; import { Toggle } from "./ui/Toggle"; +import { Checkbox } from "./ui/Checkbox"; +import { StyledTab } from "./ui/StyledTab"; interface PlaygroundProps { /** The initial squiggle string to put in the playground */ @@ -49,104 +50,46 @@ interface PlaygroundProps { showEditor?: boolean; } -const schema = yup - .object() - .shape({ - sampleCount: yup - .number() - .required() - .positive() - .integer() - .default(1000) - .min(10) - .max(1000000), - xyPointLength: yup - .number() - .required() - .positive() - .integer() - .default(1000) - .min(10) - .max(10000), - chartHeight: yup.number().required().positive().integer().default(350), - leftSizePercent: yup - .number() - .required() - .positive() - .integer() - .min(10) - .max(100) - .default(50), - showTypes: yup.boolean(), - showControls: yup.boolean(), - showSummary: yup.boolean(), - showEditor: yup.boolean(), - logX: yup.boolean(), - expY: yup.boolean(), - showSettingsPage: yup.boolean().default(false), - diagramStart: yup - .number() - .required() - .positive() - .integer() - .default(0) - .min(0), - diagramStop: yup - .number() - .required() - .positive() - .integer() - .default(10) - .min(0), - diagramCount: yup - .number() - .required() - .positive() - .integer() - .default(20) - .min(2), - }) - .required(); +const schema = yup.object({}).shape({ + sampleCount: yup + .number() + .required() + .positive() + .integer() + .default(1000) + .min(10) + .max(1000000), + xyPointLength: yup + .number() + .required() + .positive() + .integer() + .default(1000) + .min(10) + .max(10000), + chartHeight: yup.number().required().positive().integer().default(350), + leftSizePercent: yup + .number() + .required() + .positive() + .integer() + .min(10) + .max(100) + .default(50), + showTypes: yup.boolean().required(), + showControls: yup.boolean().required(), + showSummary: yup.boolean().required(), + showEditor: yup.boolean().required(), + logX: yup.boolean().required(), + expY: yup.boolean().required(), + showSettingsPage: yup.boolean().default(false), + diagramStart: yup.number().required().positive().integer().default(0).min(0), + diagramStop: yup.number().required().positive().integer().default(10).min(0), + diagramCount: yup.number().required().positive().integer().default(20).min(2), + autoplay: yup.boolean().required(), +}); -type StyledTabProps = { - name: string; - icon: (props: React.ComponentProps<"svg">) => JSX.Element; -}; - -const StyledTab: React.FC = ({ name, icon: Icon }) => { - return ( - - {({ selected }) => ( - - )} - - ); -}; +type FormFields = yup.InferType; const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({ title, @@ -187,50 +130,189 @@ function InputItem({ ); } -function Checkbox({ - name, - label, +const SamplingSettings: React.FC<{ register: UseFormRegister }> = ({ register, -}: { - name: Path; - label: string; - register: UseFormRegister; -}) { - return ( -