From 454ac0c252e6bb8d8a6cc133e9b1917eaa4944cf Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 29 Apr 2022 13:50:57 +0000 Subject: [PATCH 1/3] Add bindings to Squiggle Editor --- packages/components/package.json | 1 + .../src/components/SquiggleChart.tsx | 9 ++- .../src/components/SquiggleEditor.tsx | 81 ++++++++++++++++++- packages/components/src/index.ts | 2 + .../squiggle-lang/__tests__/TS/JS_test.ts | 22 +---- .../squiggle-lang/__tests__/TS/TestHelpers.ts | 2 +- packages/squiggle-lang/src/js/index.ts | 23 +++++- .../src/rescript/TypescriptInterface.res | 6 ++ 8 files changed, 118 insertions(+), 28 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 218747b9..e9549700 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -89,6 +89,7 @@ "@types/react": "17.0.43" }, "source": "./src/index.ts", + "browser": "dist/bundle.js", "main": "dist/src/index.js", "types": "dist/src/index.d.ts" } diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 7dda92e4..a6bf12af 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -3,10 +3,12 @@ import _ from "lodash"; import styled from "styled-components"; import { run, + runPartial, errorValueToString, squiggleExpression, + bindings, + samplingParams, } from "@quri/squiggle-lang"; -import type { samplingParams } from "@quri/squiggle-lang"; import { NumberShower } from "./NumberShower"; import { DistributionChart } from "./DistributionChart"; import { ErrorBox } from "./ErrorBox"; @@ -148,6 +150,8 @@ export interface SquiggleChartProps { /** CSS width of the element */ width?: number; height?: number; + /** Bindings of previous variables declared */ + bindings?: bindings; } export const SquiggleChart: React.FC = ({ @@ -156,6 +160,7 @@ export const SquiggleChart: React.FC = ({ outputXYPoints = 1000, onChange = () => {}, height = 60, + bindings = {}, width = NaN, }: SquiggleChartProps) => { const target = React.useRef(null); @@ -167,7 +172,7 @@ export const SquiggleChart: React.FC = ({ sampleCount: sampleCount, xyPointLength: outputXYPoints, }; - let expressionResult = run(squiggleString, samplingInputs); + let expressionResult = run(squiggleString, bindings, samplingInputs); let internal: JSX.Element; if (expressionResult.tag === "Ok") { let expression = expressionResult.value; diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 580db580..f85cf1e8 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -3,7 +3,9 @@ import * as ReactDOM from "react-dom"; import { SquiggleChart } from "./SquiggleChart"; import { CodeEditor } from "./CodeEditor"; import styled from "styled-components"; -import type { squiggleExpression } from "@quri/squiggle-lang"; +import type { squiggleExpression, bindings } from "@quri/squiggle-lang"; +import { runPartial, errorValueToString } from "@quri/squiggle-lang"; +import { ErrorBox } from "./ErrorBox"; export interface SquiggleEditorProps { /** The input string for squiggle */ @@ -26,6 +28,8 @@ export interface SquiggleEditorProps { onChange?(expr: squiggleExpression): void; /** The width of the element */ width: number; + /** Previous variable declarations */ + bindings: bindings; } const Input = styled.div` @@ -46,6 +50,7 @@ export let SquiggleEditor: React.FC = ({ diagramCount, onChange, environment, + bindings = {}, }: SquiggleEditorProps) => { let [expression, setExpression] = React.useState(initialSquiggleString); return ( @@ -71,6 +76,7 @@ export let SquiggleEditor: React.FC = ({ diagramCount={diagramCount} environment={environment} onChange={onChange} + bindings={bindings} /> ); @@ -107,3 +113,76 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) { ); return parent; } + +export interface SquigglePartialProps { + /** The input string for squiggle */ + initialSquiggleString?: string; + /** If the output requires monte carlo sampling, the amount of samples */ + sampleCount?: number; + /** The amount of points returned to draw the distribution */ + outputXYPoints?: number; + kernelWidth?: number; + pointDistLength?: 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: bindings): void; + /** The width of the element */ + width: number; + /** Previously declared variables */ + bindings: bindings; +} + +export let SquigglePartial: React.FC = ({ + initialSquiggleString = "", + onChange, + bindings, +}: SquigglePartialProps) => { + let [expression, setExpression] = React.useState(initialSquiggleString); + let squiggleResult = runPartial(expression, bindings); + if (squiggleResult.tag == "Ok") { + if (onChange) onChange(squiggleResult.value); + } + return ( +
+ + + + {squiggleResult.tag == "Error" ? ( + + {errorValueToString(squiggleResult.value)} + + ) : ( + <> + )} +
+ ); +}; + +export function renderSquigglePartialToDom(props: SquigglePartialProps) { + let parent = document.createElement("div"); + ReactDOM.render( + { + // @ts-ignore + parent.value = bindings; + + parent.dispatchEvent(new CustomEvent("input")); + if (props.onChange) props.onChange(bindings); + }} + />, + parent + ); + return parent; +} diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 364e6dcb..ffd9c8e0 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -1,7 +1,9 @@ export { SquiggleChart } from "./components/SquiggleChart"; export { SquiggleEditor, + SquigglePartial, renderSquiggleEditorToDom, + renderSquigglePartialToDom, } from "./components/SquiggleEditor"; import SquigglePlayground, { renderSquigglePlaygroundToDom, diff --git a/packages/squiggle-lang/__tests__/TS/JS_test.ts b/packages/squiggle-lang/__tests__/TS/JS_test.ts index 43f981eb..33aae7db 100644 --- a/packages/squiggle-lang/__tests__/TS/JS_test.ts +++ b/packages/squiggle-lang/__tests__/TS/JS_test.ts @@ -1,23 +1,5 @@ -import { - run, - Distribution, - resultMap, - squiggleExpression, - errorValueToString, -} from "../../src/js/index"; - -let testRun = (x: string): squiggleExpression => { - let result = run(x, { sampleCount: 100, xyPointLength: 100 }); - expect(result.tag).toEqual("Ok"); - if (result.tag === "Ok") { - return result.value; - } else { - throw Error( - "Expected squiggle expression to evaluate but got error: " + - errorValueToString(result.value) - ); - } -}; +import { Distribution, resultMap } from "../../src/js/index"; +import { testRun } from "./TestHelpers"; function Ok(x: b) { return { tag: "Ok", value: x }; diff --git a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts index 3d4153ef..8ec0c3e1 100644 --- a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts +++ b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts @@ -8,7 +8,7 @@ import { } from "../../src/js/index"; export function testRun(x: string): squiggleExpression { - let squiggleResult = run(x, { sampleCount: 1000, xyPointLength: 100 }); + let squiggleResult = run(x, {}, { sampleCount: 1000, xyPointLength: 100 }); // return squiggleResult.value if (squiggleResult.tag === "Ok") { return squiggleResult.value; diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index bc66ab99..fb67e47a 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -2,7 +2,9 @@ import * as _ from "lodash"; import { genericDist, samplingParams, - evaluate, + evaluateUsingExternalBindings, + evaluatePartialUsingExternalBindings, + externalBindings, expressionValue, errorValue, distributionError, @@ -46,7 +48,7 @@ import { Constructors_pointwiseLogarithm, Constructors_pointwisePower, } from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen"; -export type { samplingParams, errorValue }; +export type { samplingParams, errorValue, externalBindings as bindings }; export let defaultSamplingInputs: samplingParams = { sampleCount: 10000, @@ -96,15 +98,28 @@ export type squiggleExpression = export function run( squiggleString: string, + bindings?: externalBindings, samplingInputs?: samplingParams ): result { + let b = bindings ? bindings : {}; let si: samplingParams = samplingInputs ? samplingInputs : defaultSamplingInputs; - let result: result = evaluate(squiggleString); + + let result: result = + evaluateUsingExternalBindings(squiggleString, b); return resultMap(result, (x) => createTsExport(x, si)); } +// Run Partial. A partial is a block of code that doesn't return a value +export function runPartial( + squiggleString: string, + bindings: externalBindings, + _samplingInputs?: samplingParams +): result { + return evaluatePartialUsingExternalBindings(squiggleString, bindings); +} + function createTsExport( x: expressionValue, sampEnv: samplingParams @@ -166,7 +181,7 @@ function createTsExport( } } -// Helper functions to convert the recsript representations that genType doesn't +// Helper functions to convert the rescript representations that genType doesn't // cover function convertRawToTypescript( result: rescriptExport, diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index c4265178..f2758ba3 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -40,6 +40,12 @@ let evaluate = Reducer.evaluate @genType let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings +@genType +let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings + +@genType +type externalBindings = Reducer.externalBindings + @genType type expressionValue = ReducerInterface_ExpressionValue.expressionValue From 58a357cce5d5a9c9d5430e5975a3c4e634e0ef3c Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 29 Apr 2022 14:10:41 +0000 Subject: [PATCH 2/3] Add basic partial test --- .../squiggle-lang/__tests__/TS/JS_test.ts | 13 ++++++++- .../squiggle-lang/__tests__/TS/TestHelpers.ts | 28 +++++++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/squiggle-lang/__tests__/TS/JS_test.ts b/packages/squiggle-lang/__tests__/TS/JS_test.ts index 33aae7db..e522eb95 100644 --- a/packages/squiggle-lang/__tests__/TS/JS_test.ts +++ b/packages/squiggle-lang/__tests__/TS/JS_test.ts @@ -1,5 +1,5 @@ import { Distribution, resultMap } from "../../src/js/index"; -import { testRun } from "./TestHelpers"; +import { testRun, testRunPartial } from "./TestHelpers"; function Ok(x: b) { return { tag: "Ok", value: x }; @@ -57,6 +57,17 @@ describe("Record", () => { }); }); +describe("Partials", () => { + test("Can pass variables between partials and cells", () => { + let bindings = testRunPartial(`x = 5`); + let bindings2 = testRunPartial(`y = x + 2`, bindings); + expect(testRun(`y + 3`, bindings2)).toEqual({ + tag: "number", + value: 10, + }); + }); +}); + describe("Distribution", () => { //It's important that sampleCount is less than 9. If it's more, than that will create randomness //Also, note, the value should be created using makeSampleSetDist() later on. diff --git a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts index 8ec0c3e1..7d51c98e 100644 --- a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts +++ b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts @@ -1,14 +1,16 @@ import { run, - // Distribution, + runPartial, + bindings, squiggleExpression, errorValueToString, - // errorValue, - // result, } from "../../src/js/index"; -export function testRun(x: string): squiggleExpression { - let squiggleResult = run(x, {}, { sampleCount: 1000, xyPointLength: 100 }); +export function testRun(x: string, bindings = {}): squiggleExpression { + let squiggleResult = run(x, bindings, { + sampleCount: 1000, + xyPointLength: 100, + }); // return squiggleResult.value if (squiggleResult.tag === "Ok") { return squiggleResult.value; @@ -21,6 +23,22 @@ export function testRun(x: string): squiggleExpression { } } +export function testRunPartial(x: string, bindings: bindings = {}): bindings { + let squiggleResult = runPartial(x, bindings, { + sampleCount: 1000, + xyPointLength: 100, + }); + if (squiggleResult.tag === "Ok") { + return squiggleResult.value; + } else { + throw new Error( + `Expected squiggle expression to evaluate but got error: ${errorValueToString( + squiggleResult.value + )}` + ); + } +} + export function failDefault() { expect("be reached").toBe("codepath should never"); } From a866270af8f18b6f759c52350e29e0a646c346fd Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Fri, 29 Apr 2022 14:19:26 +0000 Subject: [PATCH 3/3] Update dependencies --- yarn.lock | 51 ++++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/yarn.lock b/yarn.lock index bf2d7be9..3597fea6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2506,9 +2506,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinclair/typebox@^0.23.3": - version "0.23.4" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.4.tgz#6ff93fd2585ce44f7481c9ff6af610fbb5de98a4" - integrity sha512-0/WqSvpVbCBAV1yPeko7eAczKbs78dNVAaX14quVlwOb2wxfKuXCx91h4NrEfkYK9zEnyVSW4JVI/trP3iS+Qg== + version "0.23.5" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" + integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg== "@sindresorhus/is@^0.14.0": version "0.14.0" @@ -4071,9 +4071,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^17.0.29", "@types/node@^17.0.5": - version "17.0.29" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" - integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== + version "17.0.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.30.tgz#2c6e8512acac70815e8176aa30c38025067880ef" + integrity sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw== "@types/node@^14.0.10": version "14.18.16" @@ -4140,17 +4140,10 @@ 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": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.0.tgz#b13f8d098e4b0c45df4f1ed123833143b0c71141" - integrity sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg== - dependencies: - "@types/react" "*" - -"@types/react-dom@^18.0.2": - version "18.0.2" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.2.tgz#2d6b46557aa30257e87e67a6d952146d15979d79" - integrity sha512-UxeS+Wtj5bvLRREz9tIgsK4ntCuLDo0EcAcACgw3E+9wE8ePDr9uQpq53MfcyxyIS55xJ+0B6mDS8c4qkkHLBg== +"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.2": + version "18.0.3" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.3.tgz#a022ea08c75a476fe5e96b675c3e673363853831" + integrity sha512-1RRW9kst+67gveJRYPxGmVy8eVJ05O43hg77G2j5m76/RFJtMbcfAs2viQ2UNsvvDg8F7OfQZx8qQcl6ymygaQ== dependencies: "@types/react" "*" @@ -4809,9 +4802,9 @@ acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0: integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== address@^1.0.1, address@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9" + integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== adjust-sourcemap-loader@^4.0.0: version "4.0.0" @@ -7906,9 +7899,9 @@ ejs@^3.1.6: jake "^10.8.5" electron-to-chromium@^1.4.118: - version "1.4.124" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.124.tgz#e9015e234d8632920dcdf5480351da9e845ed220" - integrity sha512-VhaE9VUYU6d2eIb+4xf83CATD+T+3bTzvxvlADkQE+c2hisiw3sZmvEDtsW704+Zky9WZGhBuQXijDVqSriQLA== + version "1.4.127" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz#4ef19d5d920abe2676d938f4170729b44f7f423a" + integrity sha512-nhD6S8nKI0O2MueC6blNOEZio+/PWppE/pevnf3LOlQA/fKPCrDp2Ao4wx4LFwmIkJpVdFdn2763YWLy9ENIZg== element-resize-detector@^1.2.2: version "1.2.4" @@ -16599,9 +16592,9 @@ terser@^4.1.2, terser@^4.6.3: source-map-support "~0.5.12" terser@^5.0.0, terser@^5.10.0, terser@^5.3.4, terser@^5.7.2: - version "5.13.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.0.tgz#d43fd71861df1b4df743980caa257c6fa03acc44" - integrity sha512-sgQ99P+fRBM1jAYzN9RTnD/xEWx/7LZgYTCRgmYriSq1wxxqiQPJgXkkLBBuwySDWJ2PP0PnVQyuf4xLUuH4Ng== + version "5.13.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799" + integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA== dependencies: acorn "^8.5.0" commander "^2.20.0" @@ -16968,9 +16961,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^4.6.3: - version "4.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" - integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== ua-parser-js@^0.7.30: version "0.7.31"