diff --git a/packages/components/src/components/DistributionChart.tsx b/packages/components/src/components/DistributionChart.tsx index 3f1b8567..620eba60 100644 --- a/packages/components/src/components/DistributionChart.tsx +++ b/packages/components/src/components/DistributionChart.tsx @@ -3,8 +3,9 @@ import { SqDistribution, result, SqDistributionError, + LabeledDistribution, resultMap, - SqRecord, + SqPlot, environment, SqDistributionTag, } from "@quri/squiggle-lang"; @@ -17,7 +18,6 @@ import { DistributionChartSpecOptions, } from "../lib/distributionSpecBuilder"; import { NumberShower } from "./NumberShower"; -import { Plot, parsePlot } from "../lib/plotParser"; import { flattenResult } from "../lib/utility"; import { hasMassBelowZero } from "../lib/distributionUtils"; @@ -28,27 +28,15 @@ export type DistributionPlottingSettings = { } & DistributionChartSpecOptions; export type DistributionChartProps = { - plot: Plot; environment: environment; width?: number; height: number; xAxisType?: "number" | "dateTime"; -} & DistributionPlottingSettings; - -export function defaultPlot(distribution: SqDistribution): Plot { - return { distributions: [{ name: "default", distribution }] }; -} - -export function makePlot(record: SqRecord): Plot | void { - const plotResult = parsePlot(record); - if (plotResult.tag === "Ok") { - return plotResult.value; - } -} +} & DistributionPlottingSettings & + ({ plot: SqPlot } | { distribution: SqDistribution }); export const DistributionChart: React.FC = (props) => { const { - plot, environment, height, showSummary, @@ -57,8 +45,14 @@ export const DistributionChart: React.FC = (props) => { actions = false, } = props; const [sized] = useSize((size) => { - const shapes = flattenResult( - plot.distributions.map((x) => + let distributions: LabeledDistribution[]; + if ("plot" in props) { + distributions = props.plot.getDistributions(); + } else { + distributions = [{ name: "default", distribution: props.distribution }]; + } + let shapes = flattenResult( + distributions.map((x) => resultMap(x.distribution.pointSet(environment), (pointSet) => ({ name: x.name, // color: x.color, // not supported yet @@ -77,7 +71,7 @@ export const DistributionChart: React.FC = (props) => { // if this is a sample set, include the samples const samples: number[] = []; - for (const { distribution } of plot?.distributions) { + for (const { distribution } of distributions) { if (distribution.tag === SqDistributionTag.SampleSet) { samples.push(...distribution.value()); } @@ -126,9 +120,9 @@ export const DistributionChart: React.FC = (props) => { /> )}
- {showSummary && plot.distributions.length === 1 && ( + {showSummary && distributions.length === 1 && ( )} diff --git a/packages/components/src/components/FunctionChart1Dist.tsx b/packages/components/src/components/FunctionChart1Dist.tsx index 16b5ad47..40aee43b 100644 --- a/packages/components/src/components/FunctionChart1Dist.tsx +++ b/packages/components/src/components/FunctionChart1Dist.tsx @@ -15,7 +15,6 @@ import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; import { DistributionChart, DistributionPlottingSettings, - defaultPlot, } from "./DistributionChart"; import { NumberShower } from "./NumberShower"; import { ErrorAlert } from "./Alert"; @@ -184,7 +183,7 @@ export const FunctionChart1Dist: React.FC = ({ mouseItem.tag === "Ok" && mouseItem.value.tag === SqValueTag.Distribution ? ( = ({ let getPercentilesMemoized = React.useMemo( () => getPercentiles({ chartSettings, fn, environment }), - [environment, fn] + [chartSettings, environment, fn] ); return ( diff --git a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx index 1c041dbf..9b5e4137 100644 --- a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx +++ b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx @@ -1,7 +1,7 @@ import React, { useContext } from "react"; import { SqDistributionTag, SqValue, SqValueTag } from "@quri/squiggle-lang"; import { NumberShower } from "../NumberShower"; -import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart"; +import { DistributionChart } from "../DistributionChart"; import { FunctionChart } from "../FunctionChart"; import clsx from "clsx"; import { VariableBox } from "./VariableBox"; @@ -104,7 +104,7 @@ export const ExpressionViewer: React.FC = ({ value, width }) => { {(settings) => { return ( = ({ value, width }) => { ); } + case SqValueTag.Plot: + const plot = value.value; + return ( + { + let disableLogX = plot.getDistributions().some((x) => { + let pointSet = x.distribution.pointSet( + getMergedSettings(value.location).environment + ); + return ( + pointSet.tag === "Ok" && + hasMassBelowZero(pointSet.value.asShape()) + ); + }); + return ( + + ); + }} + > + {(settings) => { + return ( + + ); + }} + + ); case SqValueTag.Record: - const plot = makePlot(value.value); - if (plot) { - return ( - { - let disableLogX = plot.distributions.some((x) => { - let pointSet = x.distribution.pointSet( - getMergedSettings(value.location).environment - ); - return ( - pointSet.tag === "Ok" && - hasMassBelowZero(pointSet.value.asShape()) - ); - }); - return ( - + {(_) => + value.value + .entries() + .map(([key, r]) => ( + - ); - }} - > - {(settings) => { - return ( - - ); - }} - - ); - } else { - return ( - - {(_) => - value.value - .entries() - .map(([key, r]) => ( - - )) - } - - ); - } + )) + } + + ); case SqValueTag.Array: return ( diff --git a/packages/components/src/lib/plotParser.ts b/packages/components/src/lib/plotParser.ts deleted file mode 100644 index 64e757fc..00000000 --- a/packages/components/src/lib/plotParser.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as yup from "yup"; -import { - SqValue, - SqValueTag, - SqDistribution, - result, - SqRecord, -} from "@quri/squiggle-lang"; - -export type LabeledDistribution = { - name: string; - distribution: SqDistribution; - color?: string; -}; - -export type Plot = { - distributions: LabeledDistribution[]; -}; - -function error(err: b): result { - return { tag: "Error", value: err }; -} - -function ok(x: a): result { - return { tag: "Ok", value: x }; -} - -const schema = yup - .object() - .noUnknown() - .strict() - .shape({ - distributions: yup - .array() - .required() - .of( - yup.object().required().shape({ - name: yup.string().required(), - distribution: yup.mixed().required(), - }) - ), - }); - -type JsonObject = - | string - | { [key: string]: JsonObject } - | JsonObject[] - | SqDistribution; - -function toJson(val: SqValue): JsonObject { - if (val.tag === SqValueTag.String) { - return val.value; - } else if (val.tag === SqValueTag.Record) { - return toJsonRecord(val.value); - } else if (val.tag === SqValueTag.Array) { - return val.value.getValues().map(toJson); - } else if (val.tag === SqValueTag.Distribution) { - return val.value; - } else { - throw new Error("Could not parse object of type " + val.tag); - } -} - -function toJsonRecord(val: SqRecord): JsonObject { - let recordObject: JsonObject = {}; - val.entries().forEach(([key, value]) => (recordObject[key] = toJson(value))); - return recordObject; -} - -export function parsePlot(record: SqRecord): result { - try { - const plotRecord = schema.validateSync(toJsonRecord(record)); - if (plotRecord.distributions) { - return ok({ distributions: plotRecord.distributions.map((x) => x) }); - } else { - // I have no idea why yup's typings thinks this is possible - return error("no distributions field. Should never get here"); - } - } catch (e) { - const message = e instanceof Error ? e.message : "Unknown error"; - return error(message); - } -} diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res index 6bceaaab..d69a8522 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res @@ -101,7 +101,7 @@ describe("stacktraces", () => { expect( error, - )->toBe(`Error: There are function matches for add(), but with different arguments: [add(number, number)]; [add(distribution, number)]; [add(number, distribution)]; [add(distribution, distribution)]; [add(date, duration)]; [add(duration, duration)] + )->toBe(`Error: There are function matches for add(), but with different arguments: [add(number, number)]; [add(date, duration)]; [add(duration, duration)]; [add(distribution, number)]; [add(number, distribution)]; [add(distribution, distribution)] Stack trace: f at line 4, column 5 g at line 6, column 12 diff --git a/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Plot_test.res b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Plot_test.res new file mode 100644 index 00000000..b074520f --- /dev/null +++ b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Plot_test.res @@ -0,0 +1,20 @@ +open Jest +open Reducer_TestHelpers + +describe("Plot Library", () => { + testEvalToBe( + `Plot.dist({ + show: [{ + name: "normal", + value: normal(0, 1) + }, { + name: "lognormal", + value: 1 to 2 + }, { + name: "constant", + value: 3 + }] +})`, + "Ok(Plot showing normal,lognormal,constant)", + ) +}) diff --git a/packages/squiggle-lang/src/js/SqPlot.ts b/packages/squiggle-lang/src/js/SqPlot.ts new file mode 100644 index 00000000..3a935797 --- /dev/null +++ b/packages/squiggle-lang/src/js/SqPlot.ts @@ -0,0 +1,25 @@ +import * as RSPlot from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Plot.gen"; +import { SqDistribution, wrapDistribution } from "./SqDistribution"; +import { SqValueLocation } from "./SqValueLocation"; + +type T = RSPlot.squiggleValue_Plot; + +export type LabeledDistribution = { + name: string; + distribution: SqDistribution; +}; + +export class SqPlot { + constructor(private _value: T, public location: SqValueLocation) {} + + getDistributions(): LabeledDistribution[] { + return this._value.distributions.map((v: RSPlot.labeledDistribution) => ({ + ...v, + distribution: wrapDistribution(v.distribution), + })); + } + + toString() { + return RSPlot.toString(this._value); + } +} diff --git a/packages/squiggle-lang/src/js/SqValue.ts b/packages/squiggle-lang/src/js/SqValue.ts index ef2cfca6..7e6719e0 100644 --- a/packages/squiggle-lang/src/js/SqValue.ts +++ b/packages/squiggle-lang/src/js/SqValue.ts @@ -4,6 +4,7 @@ import { wrapDistribution } from "./SqDistribution"; import { SqLambda } from "./SqLambda"; import { SqLambdaDeclaration } from "./SqLambdaDeclaration"; import { SqRecord } from "./SqRecord"; +import { SqPlot } from "./SqPlot"; import { SqArray } from "./SqArray"; import { SqValueLocation } from "./SqValueLocation"; @@ -91,6 +92,14 @@ export class SqNumberValue extends SqAbstractValue { } } +export class SqPlotValue extends SqAbstractValue { + tag = Tag.Plot as const; + + get value() { + return new SqPlot(this.valueMethod(RSValue.getPlot), this.location); + } +} + export class SqRecordValue extends SqAbstractValue { tag = Tag.Record as const; @@ -131,6 +140,7 @@ const tagToClass = { [Tag.Distribution]: SqDistributionValue, [Tag.Lambda]: SqLambdaValue, [Tag.Number]: SqNumberValue, + [Tag.Plot]: SqPlotValue, [Tag.Record]: SqRecordValue, [Tag.String]: SqStringValue, [Tag.TimeDuration]: SqTimeDurationValue, @@ -148,6 +158,7 @@ export type SqValue = | SqLambdaValue | SqNumberValue | SqRecordValue + | SqPlotValue | SqStringValue | SqTimeDurationValue | SqVoidValue; diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 886c830a..1866e414 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -6,6 +6,7 @@ export { result } from "../rescript/ForTS/ForTS_Result_tag"; export { SqDistribution, SqDistributionTag } from "./SqDistribution"; export { SqDistributionError } from "./SqDistributionError"; export { SqRecord } from "./SqRecord"; +export { SqPlot, LabeledDistribution } from "./SqPlot"; export { SqLambda } from "./SqLambda"; export { SqProject }; export { SqValue, SqValueTag }; @@ -14,7 +15,7 @@ export { defaultEnvironment, } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen"; export { SqError, SqFrame, SqLocation } from "./SqError"; -export { SqShape } from "./SqPointSetDist"; +export { SqShape, SqPoint } from "./SqPointSetDist"; export { resultMap } from "./types"; diff --git a/packages/squiggle-lang/src/rescript/FR/FR_Plot.res b/packages/squiggle-lang/src/rescript/FR/FR_Plot.res new file mode 100644 index 00000000..6100950d --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_Plot.res @@ -0,0 +1,146 @@ +open FunctionRegistry_Core +open FunctionRegistry_Helpers + +let nameSpace = "Plot" + +module FnApp = { + type fnApp<'a> = { + result: Reducer_T.value => result<'a, SqError.Message.t>, + typeRequired: frType, + } + + let fmap = (f: 'a => 'b, m: fnApp<'a>): fnApp<'b> => { + { + result: (a: Reducer_T.value) => E.R.fmap(f, m.result(a)), + typeRequired: m.typeRequired, + } + } + + module Record = { + type t<'a> = { + result: Reducer_T.map => result<'a, SqError.Message.t>, + typesRequired: array<(string, frType)>, + } + + let getField = (key: string, parser: fnApp<'a>): t<'a> => { + let func = (a: Reducer_T.map) => + switch Belt.Map.String.get(a, key) { + | Some(x) => parser.result(x) + | None => Error(impossibleError) + } + {result: func, typesRequired: [(key, parser.typeRequired)]} + } + + let merge = (m1: t<'a>, m2: t<'b>): t<('a, 'b)> => { + { + result: (a: Reducer_T.map) => E.R.merge(m1.result(a), m2.result(a)), + typesRequired: Belt.Array.concat(m1.typesRequired, m2.typesRequired), + } + } + + let fmap = (f: 'a => 'b, m: t<'a>): t<'b> => { + { + result: (a: Reducer_T.map) => E.R.fmap(f, m.result(a)), + typesRequired: m.typesRequired, + } + } + + let app = (m1: t<'a => 'b>, m2: t<'a>): t<'b> => { + { + result: (a: Reducer_T.map) => + E.R.merge(m1.result(a), m2.result(a))->E.R2.fmap(((f, x)) => f(x)), + typesRequired: Belt.Array.concat(m1.typesRequired, m2.typesRequired), + } + } + } + + let getString: fnApp = { + let func = (a: Reducer_T.value) => + switch a { + | IEvString(s) => Ok(s) + | _ => Error(impossibleError) + } + {result: func, typeRequired: FRTypeString} + } + + let getArray = (child: fnApp<'a>): fnApp> => { + let func = (a: Reducer_T.value) => + switch a { + | IEvArray(x) => x->E.A2.fmap(child.result)->E.A.R.firstErrorOrOpen + | _ => Error(impossibleError) + } + {result: func, typeRequired: FRTypeArray(child.typeRequired)} + } + let getRecord = (recMonad: Record.t<'a>): fnApp<'a> => { + let func = (a: Reducer_T.value) => + switch a { + | IEvRecord(s) => recMonad.result(s) + | _ => Error(impossibleError) + } + {result: func, typeRequired: FRTypeRecord(recMonad.typesRequired)} + } + + let getDistOrNumber: fnApp = { + let func = (a: Reducer_T.value) => + switch a { + | IEvDistribution(s) => Ok(s) + | IEvNumber(s) => Ok(GenericDist.fromFloat(s)) + | _ => Error(impossibleError) + } + {result: func, typeRequired: FRTypeDistOrNumber} + } + + let oneArgDef = ( + name: string, + arg1: fnApp<'a>, + def: 'a => result, + ): FnDefinition.t => + FnDefinition.make( + ~name, + ~inputs=[arg1.typeRequired], + ~run=(inputs, _, _) => { + E.R.bind(arg1.result(inputs[0]), def) + }, + (), + ) +} + +module Internals = { + let makeLabeledDistribution = ( + name: string, + distribution: GenericDist.t, + ): Reducer_T.labeledDistribution => {name: name, distribution: distribution} + + let getLabeledDistribution: FnApp.fnApp = { + makeLabeledDistribution + ->FnApp.Record.fmap(FnApp.Record.getField("name", FnApp.getString)) + ->FnApp.Record.app(FnApp.Record.getField("value", FnApp.getDistOrNumber)) + ->FnApp.getRecord + } + + let makePlot = (show: array): Reducer_T.plotValue => { + distributions: show, + } + + let parsePlotValue: FnApp.fnApp = { + makePlot + ->FnApp.Record.fmap(FnApp.Record.getField("show", FnApp.getArray(getLabeledDistribution))) + ->FnApp.getRecord + } +} + +let library = [ + Function.make( + ~name="dist", + ~nameSpace, + ~requiresNamespace=true, + ~output=EvtPlot, + ~examples=[ + `Plot.dist({show: [{name: "Control", value: 1 to 2}, {name: "Treatment", value: 1.5 to 2.5}]}) `, + ], + ~definitions=[ + FnApp.oneArgDef("dist", Internals.parsePlotValue, (a: Reducer_T.plotValue) => Ok(IEvPlot(a))), + ], + (), + ), +] diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res index d116b74b..72f1a4a4 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res @@ -6,6 +6,7 @@ type error = SqError.t //use type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use +@genType type squiggleValue_Plot = Reducer_T.plotValue //use // Return values are kept as they are if they are JavaScript types. @@ -30,6 +31,9 @@ external svtLambda_: string = "Lambda" @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtNumber_: string = "Number" +@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") +external svtPlot_: string = "Plot" + @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtRecord_: string = "Record" @@ -57,6 +61,7 @@ let getTag = (variant: squiggleValue): squiggleValueTag => | IEvDistribution(_) => svtDistribution_->castEnum | IEvLambda(_) => svtLambda_->castEnum | IEvNumber(_) => svtNumber_->castEnum + | IEvPlot(_) => svtPlot_->castEnum | IEvRecord(_) => svtRecord_->castEnum | IEvString(_) => svtString_->castEnum | IEvTimeDuration(_) => svtTimeDuration_->castEnum @@ -122,6 +127,13 @@ let getNumber = (variant: squiggleValue): option => | _ => None } +@genType +let getPlot = (variant: squiggleValue): option => + switch variant { + | IEvPlot(value) => value->Some + | _ => None + } + @genType let getRecord = (variant: squiggleValue): option => switch variant { diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Plot.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Plot.res new file mode 100644 index 00000000..cf5fe526 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Plot.res @@ -0,0 +1,6 @@ +type squiggleValue = ForTS_SquiggleValue.squiggleValue //use +@genType type squiggleValue_Plot = ForTS_SquiggleValue.squiggleValue_Plot //re-export recursive type +@genType type labeledDistribution = Reducer_T.labeledDistribution // use + +@genType +let toString = (v: squiggleValue_Plot) => Reducer_Value.toStringPlot(v) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts index 11d82e6d..1ceafd4e 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts @@ -6,6 +6,7 @@ export enum squiggleValueTag { Distribution = "Distribution", Lambda = "Lambda", Number = "Number", + Plot = "Plot", Record = "Record", String = "String", TimeDuration = "TimeDuration", diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index fc839e82..0778bd07 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -1,4 +1,5 @@ type internalExpressionValueType = Reducer_Value.internalExpressionValueType +let valueTypeToString = Reducer_Value.valueTypeToString type errorMessage = SqError.Message.t /* diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index 7dc7aa0b..76455969 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -1,18 +1,19 @@ let fnList = Belt.Array.concatMany([ FR_Builtin.library, + FR_Danger.library, + FR_Date.library, FR_Dict.library, FR_Dist.library, - FR_Danger.library, FR_Fn.library, - FR_Sampleset.library, - FR_List.library, - FR_Number.library, - FR_Pointset.library, - FR_Scoring.library, FR_GenericDist.library, - FR_Units.library, - FR_Date.library, + FR_List.library, FR_Math.library, + FR_Number.library, + FR_Plot.library, + FR_Pointset.library, + FR_Sampleset.library, + FR_Scoring.library, + FR_Units.library, ]) let registry = FunctionRegistry_Core.Registry.make(fnList) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res index 36025e40..605c13e4 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res @@ -9,10 +9,12 @@ type rec value = | IEvDistribution(DistributionTypes.genericDist) | IEvLambda(lambdaValue) | IEvNumber(float) + | IEvPlot(plotValue) | IEvRecord(map) | IEvString(string) | IEvTimeDuration(float) | IEvVoid + @genType.opaque and arrayValue = array @genType.opaque and map = Belt.Map.String.t and lambdaBody = (array, context, reducerFn) => value @@ -66,4 +68,12 @@ and context = { and reducerFn = (expression, context) => (value, context) +@genType and plotValue = {distributions: array} + +@genType +and labeledDistribution = { + name: string, + distribution: DistributionTypes.genericDist, +} + let topFrameName = "" diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res index 01f97647..8199456f 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res @@ -14,6 +14,7 @@ let rec toString = (aValue: T.value) => | IEvDistribution(dist) => toStringDistribution(dist) | IEvLambda(lambdaValue) => toStringLambda(lambdaValue) | IEvNumber(aNumber) => toStringNumber(aNumber) + | IEvPlot(aPlot) => toStringPlot(aPlot) | IEvRecord(aMap) => aMap->toStringRecord | IEvString(aString) => toStringString(aString) | IEvTimeDuration(t) => toStringTimeDuration(t) @@ -35,6 +36,10 @@ and toStringLambda = (lambdaValue: T.lambdaValue) => { } } and toStringNumber = aNumber => Js.String.make(aNumber) +and toStringPlot = aPlot => { + let chartNames = E.A.fmap((x: Reducer_T.labeledDistribution) => x.name, aPlot.distributions) + `Plot showing ${Js.Array2.toString(chartNames)}` +} and toStringRecord = aMap => aMap->toStringMap and toStringString = aString => `'${aString}'` and toStringSymbol = aString => `:${aString}` @@ -59,6 +64,7 @@ let toStringWithType = (aValue: T.value) => | IEvDistribution(_) => `Distribution::${toString(aValue)}` | IEvLambda(_) => `Lambda::${toString(aValue)}` | IEvNumber(_) => `Number::${toString(aValue)}` + | IEvPlot(_) => `Plot::${toString(aValue)}` | IEvRecord(_) => `Record::${toString(aValue)}` | IEvString(_) => `String::${toString(aValue)}` | IEvTimeDuration(_) => `Date::${toString(aValue)}` @@ -91,6 +97,7 @@ type internalExpressionValueType = | EvtDistribution | EvtLambda | EvtNumber + | EvtPlot | EvtRecord | EvtString | EvtTimeDuration @@ -109,6 +116,7 @@ let valueToValueType = (value: T.value) => | IEvDistribution(_) => EvtDistribution | IEvLambda(_) => EvtLambda | IEvNumber(_) => EvtNumber + | IEvPlot(_) => EvtPlot | IEvRecord(_) => EvtRecord | IEvString(_) => EvtString | IEvTimeDuration(_) => EvtTimeDuration @@ -129,6 +137,7 @@ let valueTypeToString = (valueType: internalExpressionValueType): string => | EvtDistribution => `Distribution` | EvtLambda => `Lambda` | EvtNumber => `Number` + | EvtPlot => `Plot` | EvtRecord => `Record` | EvtString => `String` | EvtTimeDuration => `Duration` diff --git a/packages/website/docs/Api/Plot.md b/packages/website/docs/Api/Plot.md new file mode 100644 index 00000000..9a7d07f0 --- /dev/null +++ b/packages/website/docs/Api/Plot.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 8 +title: Plot +--- + +Plot objects can be created to make plots of different kinds. If you wish to plot +multiple distributions simultaneously, you can use `Plot.dist`. + +**Example** + +### dist + +``` +Plot.dist({show: list({name: string, value: distribution|number})}) +``` + +```js +Plot.dist({show: [{name: "normal", value: normal(0, 1)}, {name: "lognormal", value: 2 to 3}]}) +```