diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx
index f07f5cc8..f33ccb84 100644
--- a/packages/components/src/components/SquiggleChart.tsx
+++ b/packages/components/src/components/SquiggleChart.tsx
@@ -11,12 +11,36 @@ import {
defaultImports,
defaultBindings,
defaultEnvironment,
+ declarationArg,
+ declaration,
} from "@quri/squiggle-lang";
import { NumberShower } from "./NumberShower";
import { DistributionChart } from "./DistributionChart";
import { ErrorBox } from "./ErrorBox";
import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
+function getRange(x: declaration) {
+ let first = x.args[0];
+ switch (first.tag) {
+ case "Float": {
+ return { floats: { min: first.value.min, max: first.value.max } };
+ }
+ case "Date": {
+ return { time: { min: first.value.min, max: first.value.max } };
+ }
+ }
+}
+function getChartSettings(x: declaration): FunctionChartSettings {
+ let range = getRange(x);
+ let min = range.floats ? range.floats.min : 0;
+ let max = range.floats ? range.floats.max : 10;
+ return {
+ start: min,
+ stop: max,
+ count: 20,
+ };
+}
+
const variableBox = {
Component: styled.div`
background: white;
@@ -216,6 +240,24 @@ const SquiggleItem: React.FC = ({
/>
);
+ case "lambdaDeclaration": {
+ return (
+
+
+
+ );
+ }
+ default: {
+ return <>Should be unreachable>;
+ }
}
};
diff --git a/packages/components/src/vega-specs/spec-distributions.json b/packages/components/src/vega-specs/spec-distributions.json
index 29ca4e1d..cb3db4b8 100644
--- a/packages/components/src/vega-specs/spec-distributions.json
+++ b/packages/components/src/vega-specs/spec-distributions.json
@@ -23,7 +23,7 @@
"tickOpacity": 0.0,
"domainColor": "#fff",
"domainOpacity": 0.0,
- "format": "~g",
+ "format": ".9~s",
"tickCount": 10
}
],
diff --git a/packages/components/src/vega-specs/spec-line-chart.json b/packages/components/src/vega-specs/spec-line-chart.json
index 117d9543..6180ad76 100644
--- a/packages/components/src/vega-specs/spec-line-chart.json
+++ b/packages/components/src/vega-specs/spec-line-chart.json
@@ -20,6 +20,7 @@
"name": "x",
"type": "linear",
"nice": true,
+ "zero": false,
"domain": {
"data": "facet",
"field": "x"
@@ -31,7 +32,7 @@
"type": "linear",
"range": "height",
"nice": true,
- "zero": true,
+ "zero": false,
"domain": {
"data": "facet",
"field": "y"
@@ -58,6 +59,7 @@
"tickOpacity": 0.0,
"domainColor": "#727d93",
"domainOpacity": 0.1,
+ "format": ".9~s",
"tickCount": 5
},
{
@@ -69,6 +71,7 @@
"tickOpacity": 0.0,
"domainColor": "#727d93",
"domainOpacity": 0.1,
+ "format": ".9~s",
"tickCount": 5
}
],
diff --git a/packages/components/src/vega-specs/spec-percentiles.json b/packages/components/src/vega-specs/spec-percentiles.json
index d533a866..415cc173 100644
--- a/packages/components/src/vega-specs/spec-percentiles.json
+++ b/packages/components/src/vega-specs/spec-percentiles.json
@@ -75,6 +75,7 @@
"name": "xscale",
"type": "linear",
"nice": true,
+ "zero": false,
"domain": {
"data": "facet",
"field": "x"
@@ -86,10 +87,10 @@
"type": "linear",
"range": "height",
"nice": true,
- "zero": true,
+ "zero": false,
"domain": {
"data": "facet",
- "field": "p99"
+ "fields": ["p1", "p99"]
}
}
],
@@ -113,12 +114,14 @@
"tickOpacity": 0.0,
"domainColor": "#727d93",
"domainOpacity": 0.1,
+ "format": ".9~s",
"tickCount": 5
},
{
"orient": "left",
"scale": "yscale",
"grid": false,
+ "format": ".9~s",
"labelColor": "#727d93",
"tickColor": "#fff",
"tickOpacity": 0.0,
diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts
index 8b2a46c0..03068a51 100644
--- a/packages/squiggle-lang/src/js/index.ts
+++ b/packages/squiggle-lang/src/js/index.ts
@@ -14,6 +14,8 @@ export {
errorValueToString,
distributionErrorToString,
distributionError,
+ declarationArg,
+ declaration,
} from "../rescript/TypescriptInterface.gen";
export type { errorValue, externalBindings as bindings, jsImports };
import {
@@ -185,5 +187,7 @@ function createTsExport(
return tag("date", x.value);
case "EvTimeDuration":
return tag("timeDuration", x.value);
+ case "EvDeclaration":
+ return tag("lambdaDeclaration", x.value);
}
}
diff --git a/packages/squiggle-lang/src/js/rescript_interop.ts b/packages/squiggle-lang/src/js/rescript_interop.ts
index 7bb98b5d..9f9bdb39 100644
--- a/packages/squiggle-lang/src/js/rescript_interop.ts
+++ b/packages/squiggle-lang/src/js/rescript_interop.ts
@@ -9,6 +9,8 @@ import {
discreteShape,
continuousShape,
lambdaValue,
+ lambdaDeclaration,
+ declarationArg,
} from "../rescript/TypescriptInterface.gen";
import { Distribution } from "./distribution";
import { tagged, tag } from "./types";
@@ -63,6 +65,10 @@ export type rescriptExport =
| {
TAG: 11; // EvTimeDuration
_0: number;
+ }
+ | {
+ TAG: 12; // EvDeclaration
+ _0: rescriptLambdaDeclaration;
};
type rescriptDist =
@@ -84,6 +90,23 @@ type rescriptPointSetDist =
_0: continuousShape;
};
+type rescriptLambdaDeclaration = {
+ readonly fn: lambdaValue;
+ readonly args: rescriptDeclarationArg[];
+};
+
+type rescriptDeclarationArg =
+ | {
+ TAG: 0; // Float
+ min: number;
+ max: number;
+ }
+ | {
+ TAG: 1; // Date
+ min: Date;
+ max: Date;
+ };
+
export type squiggleExpression =
| tagged<"symbol", string>
| tagged<"string", string>
@@ -96,6 +119,7 @@ export type squiggleExpression =
| tagged<"number", number>
| tagged<"date", Date>
| tagged<"timeDuration", number>
+ | tagged<"lambdaDeclaration", lambdaDeclaration>
| tagged<"record", { [key: string]: squiggleExpression }>;
export { lambdaValue };
@@ -141,6 +165,22 @@ export function convertRawToTypescript(
return tag("date", result._0);
case 11: // EvTimeDuration
return tag("number", result._0);
+ case 12: // EvDeclaration
+ return tag("lambdaDeclaration", {
+ fn: result._0.fn,
+ args: result._0.args.map(convertDeclaration),
+ });
+ }
+}
+
+function convertDeclaration(
+ declarationArg: rescriptDeclarationArg
+): declarationArg {
+ switch (declarationArg.TAG) {
+ case 0: // Float
+ return tag("Float", { min: declarationArg.min, max: declarationArg.max });
+ case 1: // Date
+ return tag("Date", { min: declarationArg.min, max: declarationArg.max });
}
}
diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res
index 99ecc78f..eed48ffc 100644
--- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res
+++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res
@@ -8,9 +8,11 @@ type rec frType =
| FRTypeNumber
| FRTypeNumeric
| FRTypeDistOrNumber
+ | FRTypeLambda
| FRTypeRecord(frTypeRecord)
- | FRTypeArray(array)
- | FRTypeOption(frType)
+ | FRTypeArray(frType)
+ | FRTypeString
+ | FRTypeVariant(array)
and frTypeRecord = array
and frTypeRecordParam = (string, frType)
@@ -21,9 +23,12 @@ and frTypeRecordParam = (string, frType)
type rec frValue =
| FRValueNumber(float)
| FRValueDist(DistributionTypes.genericDist)
- | FRValueOption(option)
+ | FRValueArray(array)
| FRValueDistOrNumber(frValueDistOrNumber)
| FRValueRecord(frValueRecord)
+ | FRValueLambda(ReducerInterface_ExpressionValue.lambdaValue)
+ | FRValueString(string)
+ | FRValueVariant(string)
and frValueRecord = array
and frValueRecordParam = (string, frValue)
and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist)
@@ -52,8 +57,10 @@ module FRType = {
let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}`
`record({${r->E.A2.fmap(input)->E.A2.joinWith(", ")}})`
}
- | FRTypeArray(r) => `record(${r->E.A2.fmap(toString)->E.A2.joinWith(", ")})`
- | FRTypeOption(v) => `option(${toString(v)})`
+ | FRTypeArray(r) => `record(${toString(r)})`
+ | FRTypeLambda => `lambda`
+ | FRTypeString => `string`
+ | FRTypeVariant(_) => "variant"
}
let rec matchWithExpressionValue = (t: t, r: expressionValue): option =>
@@ -65,7 +72,11 @@ module FRType = {
| (FRTypeDistOrNumber, EvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f)))
| (FRTypeNumeric, EvNumber(f)) => Some(FRValueNumber(f))
| (FRTypeNumeric, EvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f))
- | (FRTypeOption(v), _) => Some(FRValueOption(matchWithExpressionValue(v, r)))
+ | (FRTypeLambda, EvLambda(f)) => Some(FRValueLambda(f))
+ | (FRTypeArray(intendedType), EvArray(elements)) => {
+ let el = elements->E.A2.fmap(matchWithExpressionValue(intendedType))
+ E.A.O.openIfAllSome(el)->E.O2.fmap(r => FRValueArray(r))
+ }
| (FRTypeRecord(recordParams), EvRecord(record)) => {
let getAndMatch = (name, input) =>
E.Dict.get(record, name)->E.O.bind(matchWithExpressionValue(input))
@@ -80,6 +91,23 @@ module FRType = {
| _ => None
}
+ let rec matchReverse = (e: frValue): expressionValue =>
+ switch e {
+ | FRValueNumber(f) => EvNumber(f)
+ | FRValueDistOrNumber(FRValueNumber(n)) => EvNumber(n)
+ | FRValueDistOrNumber(FRValueDist(n)) => EvDistribution(n)
+ | FRValueDist(dist) => EvDistribution(dist)
+ | FRValueArray(elements) => EvArray(elements->E.A2.fmap(matchReverse))
+ | FRValueRecord(frValueRecord) => {
+ let record =
+ frValueRecord->E.A2.fmap(((name, value)) => (name, matchReverse(value)))->E.Dict.fromArray
+ EvRecord(record)
+ }
+ | FRValueLambda(l) => EvLambda(l)
+ | FRValueString(string) => EvString(string)
+ | FRValueVariant(string) => EvString(string)
+ }
+
let matchWithExpressionValueArray = (inputs: array, args: array): option<
array,
> => {
diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.resi b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.resi
deleted file mode 100644
index 5ca8c708..00000000
--- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.resi
+++ /dev/null
@@ -1,58 +0,0 @@
-type expressionValue = ReducerInterface_ExpressionValue.expressionValue
-
-type rec frType =
- | FRTypeNumber
- | FRTypeNumeric
- | FRTypeDistOrNumber
- | FRTypeRecord(frTypeRecord)
- | FRTypeArray(array)
- | FRTypeOption(frType)
-and frTypeRecord = array
-and frTypeRecordParam = (string, frType)
-
-type rec frValue =
- | FRValueNumber(float)
- | FRValueDist(DistributionTypes.genericDist)
- | FRValueOption(option)
- | FRValueDistOrNumber(frValueDistOrNumber)
- | FRValueRecord(frValueRecord)
-and frValueRecord = array
-and frValueRecordParam = (string, frValue)
-and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist)
-
-type fnDefinition = {
- name: string,
- inputs: array,
- run: (array, DistributionOperation.env) => result,
-}
-
-type function = {
- name: string,
- definitions: array,
-}
-
-type registry = array
-
-// Note: The function "name" is just used for documentation purposes
-module Function: {
- type t = function
- let make: (~name: string, ~definitions: array) => t
-}
-
-module FnDefinition: {
- type t = fnDefinition
- let make: (
- ~name: string,
- ~inputs: array,
- ~run: (array, DistributionOperation.env) => result,
- ) => t
-}
-
-module Registry: {
- let matchAndRun: (
- ~registry: registry,
- ~fnName: string,
- ~args: array,
- ~env: QuriSquiggleLang.DistributionOperation.env,
- ) => option>
-}
diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res
index e9b7f11c..1b0fa809 100644
--- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res
+++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res
@@ -19,6 +19,12 @@ module Prepare = {
| [FRValueRecord([(_, n1), (_, n2)])] => Ok([n1, n2])
| _ => Error(impossibleError)
}
+
+ let toArgs = (inputs: ts): result =>
+ switch inputs {
+ | [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok
+ | _ => Error(impossibleError)
+ }
}
}
@@ -30,6 +36,13 @@ module Prepare = {
}
}
+ let twoNumbers = (values: ts): result<(float, float), err> => {
+ switch values {
+ | [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2)
+ | _ => Error(impossibleError)
+ }
+ }
+
let oneDistOrNumber = (values: ts): result => {
switch values {
| [FRValueDistOrNumber(a1)] => Ok(a1)
diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res
index 43be7118..532f534d 100644
--- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res
+++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res
@@ -3,7 +3,42 @@ open FunctionRegistry_Helpers
let twoArgs = E.Tuple2.toFnCall
+module Declaration = {
+ let frType = FRTypeRecord([
+ ("fn", FRTypeLambda),
+ ("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
+ ])
+
+ let fromExpressionValue = (e: frValue): result => {
+ switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) {
+ | Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => {
+ open FunctionRegistry_Helpers.Prepare
+ let getMinMax = arg =>
+ ToValueArray.Record.toArgs([arg])
+ ->E.R.bind(ToValueTuple.twoNumbers)
+ ->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
+ inputs
+ ->E.A2.fmap(getMinMax)
+ ->E.A.R.firstErrorOrOpen
+ ->E.R2.fmap(args => ReducerInterface_ExpressionValue.EvDeclaration(
+ Declaration.make(lambda, args),
+ ))
+ }
+ | Error(r) => Error(r)
+ | Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
+ }
+ }
+}
+
let registry = [
+ Function.make(
+ ~name="FnMake",
+ ~definitions=[
+ FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
+ inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue
+ }),
+ ],
+ ),
Function.make(
~name="Normal",
~definitions=[
diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res
index c9363606..aa040fc2 100644
--- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res
+++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res
@@ -22,6 +22,7 @@ type rec expressionValue =
| EvSymbol(string)
| EvDate(Js.Date.t)
| EvTimeDuration(float)
+ | EvDeclaration(lambdaDeclaration)
and record = Js.Dict.t
and externalBindings = record
and lambdaValue = {
@@ -29,6 +30,7 @@ and lambdaValue = {
context: externalBindings,
body: internalCode,
}
+and lambdaDeclaration = Declaration.declaration
@genType
let defaultExternalBindings: externalBindings = Js.Dict.empty()
@@ -55,6 +57,7 @@ let rec toString = aValue =>
| 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)))
}
and toStringRecord = aRecord => {
let pairs =
@@ -79,6 +82,7 @@ let toStringWithType = aValue =>
| EvSymbol(_) => `Symbol::${toString(aValue)}`
| EvDate(_) => `Date::${toString(aValue)}`
| EvTimeDuration(_) => `Date::${toString(aValue)}`
+ | EvDeclaration(_) => `Declaration::${toString(aValue)}`
}
let argsToString = (args: array): string => {
@@ -124,6 +128,7 @@ type expressionValueType =
| EvtSymbol
| EvtDate
| EvtTimeDuration
+ | EvtDeclaration
type functionCallSignature = CallSignature(string, array)
type functionDefinitionSignature =
@@ -143,6 +148,7 @@ let valueToValueType = value =>
| EvSymbol(_) => EvtSymbol
| EvDate(_) => EvtDate
| EvTimeDuration(_) => EvtTimeDuration
+ | EvDeclaration(_) => EvtDeclaration
}
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
@@ -164,6 +170,7 @@ let valueTypeToString = (valueType: expressionValueType): string =>
| EvtSymbol => `Symbol`
| EvtDate => `Date`
| EvtTimeDuration => `Duration`
+ | EvtDeclaration => `Declaration`
}
let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => {
diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res
index 93af9832..932edaa1 100644
--- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res
+++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res
@@ -76,6 +76,9 @@ let distributionErrorToString = DistributionTypes.Error.toString
@genType
type lambdaValue = ReducerInterface_ExpressionValue.lambdaValue
+@genType
+type lambdaDeclaration = ReducerInterface_ExpressionValue.lambdaDeclaration
+
@genType
let defaultSamplingEnv = DistributionOperation.defaultEnv
@@ -87,3 +90,9 @@ let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment
@genType
let foreignFunctionInterface = Reducer.foreignFunctionInterface
+
+@genType
+type declarationArg = Declaration.arg
+
+@genType
+type declaration<'a> = Declaration.declaration<'a>
diff --git a/packages/squiggle-lang/src/rescript/Utility/Declaration.res b/packages/squiggle-lang/src/rescript/Utility/Declaration.res
new file mode 100644
index 00000000..871dd580
--- /dev/null
+++ b/packages/squiggle-lang/src/rescript/Utility/Declaration.res
@@ -0,0 +1,42 @@
+@genType
+type arg = Float({min: float, max: float}) | Date({min: Js.Date.t, max: Js.Date.t})
+
+@genType
+type declaration<'a> = {
+ fn: 'a,
+ args: array,
+}
+
+module ContinuousFloatArg = {
+ let make = (min: float, max: float): arg => {
+ Float({min: min, max: max})
+ }
+}
+
+module ContinuousTimeArg = {
+ let make = (min: Js.Date.t, max: Js.Date.t): arg => {
+ Date({min: min, max: max})
+ }
+}
+
+module Arg = {
+ let toString = (arg: arg) => {
+ switch arg {
+ | Float({min, max}) =>
+ `Float({min: ${E.Float.with2DigitsPrecision(min)}, max: ${E.Float.with2DigitsPrecision(
+ max,
+ )}})`
+ | Date({min, max}) =>
+ `Date({min: ${DateTime.Date.toString(min)}, max: ${DateTime.Date.toString(max)}})`
+ }
+ }
+}
+
+let make = (fn: 'a, args: array): declaration<'a> => {
+ {fn: fn, args: args}
+}
+
+let toString = (r: declaration<'a>, fnToString): string => {
+ let args = r.args->E.A2.fmap(Arg.toString) |> E.A.joinWith(", ")
+ return`fn: ${fnToString(r.fn)}, args: [${args}]`
+}
diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res
index 2c45a8b2..40852eb3 100644
--- a/packages/squiggle-lang/src/rescript/Utility/E.res
+++ b/packages/squiggle-lang/src/rescript/Utility/E.res
@@ -870,4 +870,5 @@ module Dict = {
type t<'a> = Js.Dict.t<'a>
let get = Js.Dict.get
let keys = Js.Dict.keys
+ let fromArray = Js.Dict.fromArray
}