Merge branch 'develop' into multiple-charts
This commit is contained in:
commit
ba59047d31
|
@ -1,5 +1,10 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang";
|
import {
|
||||||
|
lambdaValue,
|
||||||
|
environment,
|
||||||
|
runForeign,
|
||||||
|
errorValueToString,
|
||||||
|
} from "@quri/squiggle-lang";
|
||||||
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
||||||
import { FunctionChart1Number } from "./FunctionChart1Number";
|
import { FunctionChart1Number } from "./FunctionChart1Number";
|
||||||
import { DistributionPlottingSettings } from "./DistributionChart";
|
import { DistributionPlottingSettings } from "./DistributionChart";
|
||||||
|
@ -45,10 +50,16 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const validResult = getValidResult();
|
const validResult = getValidResult();
|
||||||
const resultType =
|
|
||||||
validResult.tag === "Ok" ? validResult.value.tag : ("Error" as const);
|
|
||||||
|
|
||||||
switch (resultType) {
|
if (validResult.tag === "Error") {
|
||||||
|
return (
|
||||||
|
<ErrorAlert heading="Error">
|
||||||
|
{errorValueToString(validResult.value)}
|
||||||
|
</ErrorAlert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (validResult.value.tag) {
|
||||||
case "distribution":
|
case "distribution":
|
||||||
return (
|
return (
|
||||||
<FunctionChart1Dist
|
<FunctionChart1Dist
|
||||||
|
@ -68,15 +79,11 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
||||||
height={height}
|
height={height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "Error":
|
|
||||||
return (
|
|
||||||
<ErrorAlert heading="Error">The function failed to be run</ErrorAlert>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<MessageAlert heading="Function Display Not Supported">
|
<MessageAlert heading="Function Display Not Supported">
|
||||||
There is no function visualization for this type of output:{" "}
|
There is no function visualization for this type of output:{" "}
|
||||||
<span className="font-bold">{resultType}</span>
|
<span className="font-bold">{validResult.value.tag}</span>
|
||||||
</MessageAlert>
|
</MessageAlert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
|
||||||
let chartPointsData: point[] = chartPointsToRender.map((x) => {
|
let chartPointsData: point[] = chartPointsToRender.map((x) => {
|
||||||
let result = runForeign(fn, [x], environment);
|
let result = runForeign(fn, [x], environment);
|
||||||
if (result.tag === "Ok") {
|
if (result.tag === "Ok") {
|
||||||
if (result.value.tag == "distribution") {
|
if (result.value.tag === "distribution") {
|
||||||
return { x, value: { tag: "Ok", value: result.value.value } };
|
return { x, value: { tag: "Ok", value: result.value.value } };
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
@ -166,12 +166,14 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
||||||
setMouseOverlay(NaN);
|
setMouseOverlay(NaN);
|
||||||
}
|
}
|
||||||
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
|
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
|
||||||
|
|
||||||
|
//TODO: This custom error handling is a bit hacky and should be improved.
|
||||||
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
|
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
|
||||||
? runForeign(fn, [mouseOverlay], environment)
|
? runForeign(fn, [mouseOverlay], environment)
|
||||||
: {
|
: {
|
||||||
tag: "Error",
|
tag: "Error",
|
||||||
value: {
|
value: {
|
||||||
tag: "REExpectedType",
|
tag: "RETodo",
|
||||||
value: "Hover x-coordinate returned NaN. Expected a number.",
|
value: "Hover x-coordinate returned NaN. Expected a number.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -232,7 +232,7 @@ export function buildVegaSpec(
|
||||||
},
|
},
|
||||||
size: [{ value: 100 }],
|
size: [{ value: 100 }],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
signal: "datum.y",
|
signal: "{ probability: datum.y, value: datum.x }",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
|
|
|
@ -21,6 +21,10 @@ describe("builtin", () => {
|
||||||
"addOne(t)=t+1; toList(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))",
|
"addOne(t)=t+1; toList(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))",
|
||||||
"Ok([2,3,4,5,6,7])",
|
"Ok([2,3,4,5,6,7])",
|
||||||
)
|
)
|
||||||
|
testEval(
|
||||||
|
"toList(mapSamplesN([fromSamples([1,2,3,4,5,6]), fromSamples([6, 5, 4, 3, 2, 1])], {|x| x[0] > x[1] ? x[0] : x[1]}))",
|
||||||
|
"Ok([6,5,4,4,5,6])",
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("builtin exception", () => {
|
describe("builtin exception", () => {
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("high priority modifier", () => {
|
describe("high priority contract", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"answer: number<-min<-max(100)|string",
|
"answer: number<-min<-max(100)|string",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
||||||
|
@ -30,7 +30,7 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("low priority modifier", () => {
|
describe("low priority contract", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"answer: number | string $ opaque",
|
"answer: number | string $ opaque",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
|
@ -63,14 +63,14 @@ describe("Peggy parse type", () => {
|
||||||
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("type paranthesis", () => {
|
describe("type parenthesis", () => {
|
||||||
//$ is introduced to avoid paranthesis
|
//$ is introduced to avoid parenthesis
|
||||||
testParse(
|
testParse(
|
||||||
"answer: (number|string)<-opaque",
|
"answer: (number|string)<-opaque",
|
||||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("squiggle expressions in type modifiers", () => {
|
describe("squiggle expressions in type contracts", () => {
|
||||||
testParse(
|
testParse(
|
||||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||||
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
||||||
|
|
|
@ -3,11 +3,10 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Reducer_Peggy_TestHelpers
|
open Reducer_Peggy_TestHelpers
|
||||||
open Expect
|
|
||||||
|
|
||||||
describe("Peggy to Expression", () => {
|
describe("Peggy to Expression", () => {
|
||||||
describe("literals operators parenthesis", () => {
|
describe("literals operators parenthesis", () => {
|
||||||
// Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement
|
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
|
||||||
testToExpression("1", "{1}", ~v="1", ())
|
testToExpression("1", "{1}", ~v="1", ())
|
||||||
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
|
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
|
||||||
testToExpression("true", "{true}", ~v="true", ())
|
testToExpression("true", "{true}", ~v="true", ())
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("high priority modifier", () => {
|
describe("high priority contract", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"answer: number<-min(1)<-max(100)|string",
|
"answer: number<-min(1)<-max(100)|string",
|
||||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
||||||
|
@ -78,7 +78,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("low priority modifier", () => {
|
describe("low priority contract", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"answer: number | string $ opaque",
|
"answer: number | string $ opaque",
|
||||||
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
||||||
|
@ -86,7 +86,7 @@ describe("Peggy Types to Expression", () => {
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("squiggle expressions in type modifiers", () => {
|
describe("squiggle expressions in type contracts", () => {
|
||||||
testToExpression(
|
testToExpression(
|
||||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||||
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
|
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeCompile = Reducer_Type_Compile
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let myIevEval = (aTypeSourceCode: string) =>
|
||||||
|
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||||
|
let myIevEvalToString = (aTypeSourceCode: string) =>
|
||||||
|
myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
|
||||||
|
|
||||||
|
let myIevExpectEqual = (aTypeSourceCode, answer) =>
|
||||||
|
expect(myIevEvalToString(aTypeSourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myIevTest = (test, aTypeSourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
|
||||||
|
|
||||||
|
let myTypeEval = (aTypeSourceCode: string) =>
|
||||||
|
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||||
|
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
|
||||||
|
|
||||||
|
let myTypeExpectEqual = (aTypeSourceCode, answer) =>
|
||||||
|
expect(myTypeEvalToString(aTypeSourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myTypeTest = (test, aTypeSourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myTypeExpectEqual(aTypeSourceCode, answer))
|
||||||
|
|
||||||
|
// | ItTypeIdentifier(string)
|
||||||
|
myTypeTest(test, "number", "number")
|
||||||
|
myTypeTest(test, "(number)", "number")
|
||||||
|
// | ItModifiedType({modifiedType: iType})
|
||||||
|
myIevTest(test, "number<-min(0)", "Ok({min: 0,typeIdentifier: #number,typeTag: 'typeIdentifier'})")
|
||||||
|
myTypeTest(test, "number<-min(0)", "number<-min(0)")
|
||||||
|
// | ItTypeOr({typeOr: array<iType>})
|
||||||
|
myTypeTest(test, "number | string", "(number | string)")
|
||||||
|
// | ItTypeFunction({inputs: array<iType>, output: iType})
|
||||||
|
myTypeTest(test, "number => number => number", "(number => number => number)")
|
||||||
|
// | ItTypeArray({element: iType})
|
||||||
|
myIevTest(test, "[number]", "Ok({element: #number,typeTag: 'typeArray'})")
|
||||||
|
myTypeTest(test, "[number]", "[number]")
|
||||||
|
// | ItTypeTuple({elements: array<iType>})
|
||||||
|
myTypeTest(test, "[number, string]", "[number, string]")
|
||||||
|
// | ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
||||||
|
myIevTest(
|
||||||
|
test,
|
||||||
|
"{age: number, name: string}",
|
||||||
|
"Ok({properties: {age: #number,name: #string},typeTag: 'typeRecord'})",
|
||||||
|
)
|
||||||
|
myTypeTest(test, "{age: number, name: string}", "{age: number, name: string}")
|
|
@ -0,0 +1,41 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeChecker = Reducer_Type_TypeChecker
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
|
||||||
|
'v,
|
||||||
|
ErrorValue.t,
|
||||||
|
> => {
|
||||||
|
let reducerFn = Expression.reduceExpression
|
||||||
|
let rResult =
|
||||||
|
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||||
|
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||||
|
)
|
||||||
|
rResult->Belt.Result.flatMap(result =>
|
||||||
|
switch result {
|
||||||
|
| IEvArray(args) => TypeChecker.checkArguments(aTypeSourceCode, args, reducerFn)
|
||||||
|
| _ => Js.Exn.raiseError("Arguments has to be an array")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myCheckArguments = (aTypeSourceCode: string, sourceCode: string): string =>
|
||||||
|
switch checkArgumentsSourceCode(aTypeSourceCode, sourceCode) {
|
||||||
|
| Ok(_) => "Ok"
|
||||||
|
| Error(error) => ErrorValue.errorToString(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myCheckArgumentsExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
expect(myCheckArguments(aTypeSourceCode, sourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myCheckArgumentsTest = (test, aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myCheckArgumentsExpectEqual(aTypeSourceCode, sourceCode, answer))
|
||||||
|
|
||||||
|
myCheckArgumentsTest(test, "number=>number=>number", "[1,2]", "Ok")
|
|
@ -0,0 +1,70 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
module TypeChecker = Reducer_Type_TypeChecker
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
// In development, you are expected to use TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn).
|
||||||
|
// isTypeOfSourceCode is written to use strings instead of expression values.
|
||||||
|
|
||||||
|
let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
|
||||||
|
'v,
|
||||||
|
ErrorValue.t,
|
||||||
|
> => {
|
||||||
|
let reducerFn = Expression.reduceExpression
|
||||||
|
let rResult =
|
||||||
|
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||||
|
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||||
|
)
|
||||||
|
rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
|
||||||
|
}
|
||||||
|
|
||||||
|
let myTypeCheck = (aTypeSourceCode: string, sourceCode: string): string =>
|
||||||
|
switch isTypeOfSourceCode(aTypeSourceCode, sourceCode) {
|
||||||
|
| Ok(_) => "Ok"
|
||||||
|
| Error(error) => ErrorValue.errorToString(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let myTypeCheckExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
expect(myTypeCheck(aTypeSourceCode, sourceCode))->toEqual(answer)
|
||||||
|
|
||||||
|
let myTypeCheckTest = (test, aTypeSourceCode, sourceCode, answer) =>
|
||||||
|
test(aTypeSourceCode, () => myTypeCheckExpectEqual(aTypeSourceCode, sourceCode, answer))
|
||||||
|
|
||||||
|
myTypeCheckTest(test, "number", "1", "Ok")
|
||||||
|
myTypeCheckTest(test, "number", "'2'", "Expected type: number but got: '2'")
|
||||||
|
myTypeCheckTest(test, "string", "3", "Expected type: string but got: 3")
|
||||||
|
myTypeCheckTest(test, "string", "'a'", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number]", "[1,2,3]", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number]", "['a','a','a']", "Expected type: number but got: 'a'")
|
||||||
|
myTypeCheckTest(test, "[number]", "[1,'a',3]", "Expected type: number but got: 'a'")
|
||||||
|
myTypeCheckTest(test, "[number, string]", "[1,'a']", "Ok")
|
||||||
|
myTypeCheckTest(test, "[number, string]", "[1, 2]", "Expected type: string but got: 2")
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"[number, string, string]",
|
||||||
|
"[1,'a']",
|
||||||
|
"Expected type: [number, string, string] but got: [1,'a']",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"[number, string]",
|
||||||
|
"[1,'a', 3]",
|
||||||
|
"Expected type: [number, string] but got: [1,'a',3]",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(test, "{age: number, name: string}", "{age: 1, name: 'a'}", "Ok")
|
||||||
|
myTypeCheckTest(
|
||||||
|
test,
|
||||||
|
"{age: number, name: string}",
|
||||||
|
"{age: 1, name: 'a', job: 'IT'}",
|
||||||
|
"Expected type: {age: number, name: string} but got: {age: 1,job: 'IT',name: 'a'}",
|
||||||
|
)
|
||||||
|
myTypeCheckTest(test, "number | string", "1", "Ok")
|
||||||
|
myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1")
|
||||||
|
myTypeCheckTest(test, "number<-min(10)", "10", "Ok")
|
||||||
|
myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0")
|
|
@ -10,5 +10,5 @@ describe("Evaluate ternary operator", () => {
|
||||||
testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
testEvalToBe("2 > 1 ? 'YES' : 'NO'", "Ok('YES')")
|
testEvalToBe("2 > 1 ? 'YES' : 'NO'", "Ok('YES')")
|
||||||
testEvalToBe("2 <= 1 ? 'YES' : 'NO'", "Ok('NO')")
|
testEvalToBe("2 <= 1 ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
testEvalToBe("1+1 ? 'YES' : 'NO'", "Error(Expected type: Boolean)")
|
testEvalToBe("1+1 ? 'YES' : 'NO'", "Error(Expected type: Boolean but got: )")
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
let expectEvalToBeOk = (expr: string) =>
|
||||||
|
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
|
||||||
|
|
||||||
|
let registry = FunctionRegistry_Library.registry
|
||||||
|
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
|
||||||
|
|
||||||
|
describe("FunctionRegistry Library", () => {
|
||||||
|
describe("Regular tests", () => {
|
||||||
|
testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])")
|
||||||
|
testEvalToBe("make(3, 'HI')", "Error(Function not found: make(Number,String))")
|
||||||
|
testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])")
|
||||||
|
testEvalToBe("List.first([3,5,8])", "Ok(3)")
|
||||||
|
testEvalToBe("List.last([3,5,8])", "Ok(8)")
|
||||||
|
testEvalToBe("List.reverse([3,5,8])", "Ok([8,5,3])")
|
||||||
|
testEvalToBe("Dist.normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal(5,2)", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("normal({mean:5,stdev:2})", "Ok(Normal(5,2))")
|
||||||
|
testEvalToBe("-2 to 4", "Ok(Normal(1,1.8238704957353074))")
|
||||||
|
testEvalToBe("pointMass(5)", "Ok(PointMass(5))")
|
||||||
|
testEvalToBe("Number.floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("Number.ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("floor(5.5)", "Ok(5)")
|
||||||
|
testEvalToBe("ceil(5.5)", "Ok(6)")
|
||||||
|
testEvalToBe("Number.abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("abs(5.5)", "Ok(5.5)")
|
||||||
|
testEvalToBe("Number.exp(10)", "Ok(22026.465794806718)")
|
||||||
|
testEvalToBe("Number.log10(10)", "Ok(1)")
|
||||||
|
testEvalToBe("Number.log2(10)", "Ok(3.321928094887362)")
|
||||||
|
testEvalToBe("Number.sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("sum([2,5,3])", "Ok(10)")
|
||||||
|
testEvalToBe("Number.product([2,5,3])", "Ok(30)")
|
||||||
|
testEvalToBe("Number.min([2,5,3])", "Ok(2)")
|
||||||
|
testEvalToBe("Number.max([2,5,3])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.mean([0,5,10])", "Ok(5)")
|
||||||
|
testEvalToBe("Number.geomean([1,5,18])", "Ok(4.481404746557164)")
|
||||||
|
testEvalToBe("Number.stdev([0,5,10,15])", "Ok(5.5901699437494745)")
|
||||||
|
testEvalToBe("Number.variance([0,5,10,15])", "Ok(31.25)")
|
||||||
|
testEvalToBe("Number.sort([10,0,15,5])", "Ok([0,5,10,15])")
|
||||||
|
testEvalToBe("Number.cumsum([1,5,3])", "Ok([1,6,9])")
|
||||||
|
testEvalToBe("Number.cumprod([1,5,3])", "Ok([1,5,15])")
|
||||||
|
testEvalToBe("Number.diff([1,5,3])", "Ok([4,-2])")
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Ok(-0.33591375663884876)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Ok(0.32244107041564646)",
|
||||||
|
)
|
||||||
|
testEvalToBe("Dist.logScore({estimate: normal(5,2), answer: 4.5})", "Ok(1.6433360626394853)")
|
||||||
|
testEvalToBe("Dist.klDivergence(normal(5,2), normal(5,1.5))", "Ok(0.06874342818671068)")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fn auto-testing", () => {
|
||||||
|
testAll("tests of validity", examples, r => {
|
||||||
|
expectEvalToBeOk(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
testAll(
|
||||||
|
"tests of type",
|
||||||
|
E.A.to_list(
|
||||||
|
FunctionRegistry_Core.Registry.allExamplesWithFns(registry)->E.A2.filter(((fn, _)) =>
|
||||||
|
E.O.isSome(fn.output)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
((fn, example)) => {
|
||||||
|
let responseType =
|
||||||
|
example
|
||||||
|
->Reducer.evaluate
|
||||||
|
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
|
||||||
|
let expectedOutputType = fn.output |> E.O.toExn("")
|
||||||
|
expect(responseType)->toEqual(Ok(expectedOutputType))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
|
@ -117,6 +117,11 @@ let map3 = (
|
||||||
): result<t, sampleSetError> =>
|
): result<t, sampleSetError> =>
|
||||||
E.A.zip3(get(t1), get(t2), get(t3))->E.A2.fmap(E.Tuple3.toFnCall(fn))->_fromSampleResultArray
|
E.A.zip3(get(t1), get(t2), get(t3))->E.A2.fmap(E.Tuple3.toFnCall(fn))->_fromSampleResultArray
|
||||||
|
|
||||||
|
let mapN = (~fn: array<float> => result<float, Operation.Error.t>, ~t1: array<t>): result<
|
||||||
|
t,
|
||||||
|
sampleSetError,
|
||||||
|
> => E.A.transpose(E.A.fmap(get, t1))->E.A2.fmap(fn)->_fromSampleResultArray
|
||||||
|
|
||||||
let mean = t => T.get(t)->E.A.Floats.mean
|
let mean = t => T.get(t)->E.A.Floats.mean
|
||||||
let geomean = t => T.get(t)->E.A.Floats.geomean
|
let geomean = t => T.get(t)->E.A.Floats.geomean
|
||||||
let mode = t => T.get(t)->E.A.Floats.mode
|
let mode = t => T.get(t)->E.A.Floats.mode
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
||||||
|
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function Registry "Type". A type, without any other information.
|
Function Registry "Type". A type, without any other information.
|
||||||
|
@ -42,18 +43,26 @@ and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.g
|
||||||
type fnDefinition = {
|
type fnDefinition = {
|
||||||
name: string,
|
name: string,
|
||||||
inputs: array<frType>,
|
inputs: array<frType>,
|
||||||
run: (array<frValue>, GenericDist.env) => result<internalExpressionValue, string>,
|
run: (
|
||||||
|
array<internalExpressionValue>,
|
||||||
|
array<frValue>,
|
||||||
|
GenericDist.env,
|
||||||
|
) => result<internalExpressionValue, string>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type function = {
|
type function = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<fnDefinition>,
|
definitions: array<fnDefinition>,
|
||||||
examples: option<string>,
|
requiresNamespace: bool,
|
||||||
|
nameSpace: string,
|
||||||
|
output: option<internalExpressionValueType>,
|
||||||
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type registry = array<function>
|
type fnNameDict = Js.Dict.t<array<function>>
|
||||||
|
type registry = {functions: array<function>, fnNameDict: fnNameDict}
|
||||||
|
|
||||||
module FRType = {
|
module FRType = {
|
||||||
type t = frType
|
type t = frType
|
||||||
|
@ -265,7 +274,7 @@ module Matcher = {
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findExactMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
|
||||||
fullMatch->E.O.bind(((fn, match)) =>
|
fullMatch->E.O.bind(((fn, match)) =>
|
||||||
switch match {
|
switch match {
|
||||||
|
@ -276,7 +285,7 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let _findNameMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
let functionMatchPairs = r->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
let functionMatchPairs = r.functions->E.A2.fmap(l => (l, Function.match(l, fnName, args)))
|
||||||
let getNameMatches =
|
let getNameMatches =
|
||||||
functionMatchPairs
|
functionMatchPairs
|
||||||
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
|
||||||
|
@ -295,10 +304,13 @@ module Matcher = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
|
||||||
switch _findExactMatches(r, fnName, args) {
|
let fnNameInParts = Js.String.split(".", fnName)
|
||||||
|
let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0])
|
||||||
|
|
||||||
|
switch _findExactMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.FullMatch(r)
|
| Some(r) => Match.FullMatch(r)
|
||||||
| None =>
|
| None =>
|
||||||
switch _findNameMatches(r, fnName, args) {
|
switch _findNameMatches(r, fnToSearch, args) {
|
||||||
| Some(r) => Match.SameNameDifferentArguments(r)
|
| Some(r) => Match.SameNameDifferentArguments(r)
|
||||||
| None => Match.DifferentName
|
| None => Match.DifferentName
|
||||||
}
|
}
|
||||||
|
@ -308,7 +320,7 @@ module Matcher = {
|
||||||
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
let matchToDef = (registry: registry, {fnName, inputIndex}: RegistryMatch.match): option<
|
||||||
fnDefinition,
|
fnDefinition,
|
||||||
> =>
|
> =>
|
||||||
registry
|
registry.functions
|
||||||
->E.A.getBy(fn => fn.name === fnName)
|
->E.A.getBy(fn => fn.name === fnName)
|
||||||
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
|
||||||
}
|
}
|
||||||
|
@ -322,15 +334,23 @@ module FnDefinition = {
|
||||||
t.name ++ `(${inputs})`
|
t.name ++ `(${inputs})`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMatch = (t: t, args: array<internalExpressionValue>) => {
|
||||||
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
|
switch argValues {
|
||||||
|
| Some(_) => true
|
||||||
|
| None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
|
let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
|
||||||
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
switch argValues {
|
switch argValues {
|
||||||
| Some(values) => t.run(values, env)
|
| Some(values) => t.run(args, values, env)
|
||||||
| None => Error("Incorrect Types")
|
| None => Error("Incorrect Types")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~inputs, ~run): t => {
|
let make = (~name, ~inputs, ~run, ()): t => {
|
||||||
name: name,
|
name: name,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
run: run,
|
run: run,
|
||||||
|
@ -343,16 +363,29 @@ module Function = {
|
||||||
type functionJson = {
|
type functionJson = {
|
||||||
name: string,
|
name: string,
|
||||||
definitions: array<string>,
|
definitions: array<string>,
|
||||||
examples: option<string>,
|
examples: array<string>,
|
||||||
description: option<string>,
|
description: option<string>,
|
||||||
isExperimental: bool,
|
isExperimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (~name, ~definitions, ~examples=?, ~description=?, ~isExperimental=false, ()): t => {
|
let make = (
|
||||||
|
~name,
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~definitions,
|
||||||
|
~examples=?,
|
||||||
|
~output=?,
|
||||||
|
~description=?,
|
||||||
|
~isExperimental=false,
|
||||||
|
(),
|
||||||
|
): t => {
|
||||||
name: name,
|
name: name,
|
||||||
|
nameSpace: nameSpace,
|
||||||
definitions: definitions,
|
definitions: definitions,
|
||||||
examples: examples,
|
output: output,
|
||||||
|
examples: examples |> E.O.default([]),
|
||||||
isExperimental: isExperimental,
|
isExperimental: isExperimental,
|
||||||
|
requiresNamespace: requiresNamespace,
|
||||||
description: description,
|
description: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,22 +398,64 @@ module Function = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module NameSpace = {
|
||||||
|
type t = {name: string, functions: array<function>}
|
||||||
|
let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany
|
||||||
|
let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq
|
||||||
|
let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name)
|
||||||
|
}
|
||||||
|
|
||||||
module Registry = {
|
module Registry = {
|
||||||
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
|
let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson)
|
||||||
|
let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany
|
||||||
|
let allExamplesWithFns = (r: registry) =>
|
||||||
|
r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany
|
||||||
|
|
||||||
|
let _buildFnNameDict = (r: array<function>): fnNameDict => {
|
||||||
|
let allDefinitionsWithFns =
|
||||||
|
r
|
||||||
|
->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions)))
|
||||||
|
->E.A.concatMany
|
||||||
|
let functionsWithFnNames =
|
||||||
|
allDefinitionsWithFns
|
||||||
|
->E.A2.fmap(((fn, def)) => {
|
||||||
|
let nameWithNamespace = `${fn.nameSpace}.${def.name}`
|
||||||
|
let nameWithoutNamespace = def.name
|
||||||
|
fn.requiresNamespace
|
||||||
|
? [(nameWithNamespace, fn)]
|
||||||
|
: [(nameWithNamespace, fn), (nameWithoutNamespace, fn)]
|
||||||
|
})
|
||||||
|
->E.A.concatMany
|
||||||
|
let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq
|
||||||
|
let cacheAsArray: array<(string, array<function>)> = uniqueNames->E.A2.fmap(uniqueName => {
|
||||||
|
let relevantItems =
|
||||||
|
E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap(
|
||||||
|
E.Tuple2.second,
|
||||||
|
)
|
||||||
|
(uniqueName, relevantItems)
|
||||||
|
})
|
||||||
|
cacheAsArray->Js.Dict.fromArray
|
||||||
|
}
|
||||||
|
|
||||||
|
let make = (fns: array<function>): registry => {
|
||||||
|
let dict = _buildFnNameDict(fns)
|
||||||
|
{functions: fns, fnNameDict: dict}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
There's a (potential+minor) bug here: If a function definition is called outside of the calls
|
||||||
to the registry, then it's possible that there could be a match after the registry is
|
to the registry, then it's possible that there could be a match after the registry is
|
||||||
called. However, for now, we could just call the registry last.
|
called. However, for now, we could just call the registry last.
|
||||||
*/
|
*/
|
||||||
let matchAndRun = (
|
let _matchAndRun = (
|
||||||
~registry: registry,
|
~registry: registry,
|
||||||
~fnName: string,
|
~fnName: string,
|
||||||
~args: array<internalExpressionValue>,
|
~args: array<internalExpressionValue>,
|
||||||
~env: GenericDist.env,
|
~env: GenericDist.env,
|
||||||
) => {
|
) => {
|
||||||
|
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
|
||||||
|
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
|
||||||
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
|
||||||
//Js.log(toSimple(registry))
|
|
||||||
let showNameMatchDefinitions = matches => {
|
let showNameMatchDefinitions = matches => {
|
||||||
let defs =
|
let defs =
|
||||||
matches
|
matches
|
||||||
|
@ -391,10 +466,21 @@ module Registry = {
|
||||||
->E.A2.joinWith("; ")
|
->E.A2.joinWith("; ")
|
||||||
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
|
||||||
}
|
}
|
||||||
switch Matcher.Registry.findMatches(registry, fnName, args) {
|
|
||||||
|
switch Matcher.Registry.findMatches(modified, fnName, args) {
|
||||||
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
| Matcher.Match.FullMatch(match) => match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env))
|
||||||
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dispatch = (
|
||||||
|
registry,
|
||||||
|
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
|
||||||
|
env,
|
||||||
|
) => {
|
||||||
|
_matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
||||||
|
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,72 +213,3 @@ module Process = {
|
||||||
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
twoValues(~fn=Helpers.wrapSymbolic(fn), ~values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module TwoArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordP5P95 = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let makeRecordMeanStdev = (name, fn) => {
|
|
||||||
FnDefinition.make(
|
|
||||||
~name,
|
|
||||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module OneArgDist = {
|
|
||||||
let process = (~fn, ~env, r) =>
|
|
||||||
r
|
|
||||||
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
|
||||||
->E.R2.fmap(Wrappers.evDistribution)
|
|
||||||
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber], ~run=(inputs, env) =>
|
|
||||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
module ArrayNumberDist = {
|
|
||||||
let make = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeNumber)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let make2 = (name, fn) => {
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeArray(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
Prepare.ToTypedArray.numbers(inputs)
|
|
||||||
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
|
||||||
->E.R.bind(fn)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module NumberToNumber = {
|
|
||||||
let make = (name, fn) =>
|
|
||||||
FnDefinition.make(~name, ~inputs=[FRTypeNumber], ~run=(inputs, _) => {
|
|
||||||
inputs
|
|
||||||
->getOrError(0)
|
|
||||||
->E.R.bind(Prepare.oneNumber)
|
|
||||||
->E.R2.fmap(fn)
|
|
||||||
->E.R2.fmap(Wrappers.evNumber)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,576 +1,12 @@
|
||||||
open FunctionRegistry_Core
|
let fnList = Belt.Array.concatMany([
|
||||||
open FunctionRegistry_Helpers
|
FR_Dict.library,
|
||||||
|
FR_Dist.library,
|
||||||
|
FR_Fn.library,
|
||||||
|
FR_List.library,
|
||||||
|
FR_Number.library,
|
||||||
|
FR_Pointset.library,
|
||||||
|
FR_Scoring.library,
|
||||||
|
])
|
||||||
|
|
||||||
let twoArgs = E.Tuple2.toFnCall
|
let registry = FunctionRegistry_Core.Registry.make(fnList)
|
||||||
|
let dispatch = FunctionRegistry_Core.Registry.dispatch(registry)
|
||||||
module Declaration = {
|
|
||||||
let frType = FRTypeRecord([
|
|
||||||
("fn", FRTypeLambda),
|
|
||||||
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
|
||||||
])
|
|
||||||
|
|
||||||
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
|
||||||
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_InternalExpressionValue.IEvDeclaration(
|
|
||||||
Declaration.make(lambda, args),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
| Error(r) => Error(r)
|
|
||||||
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
|
||||||
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
|
||||||
let xyCoords =
|
|
||||||
array->E.R.bind(xyCoords =>
|
|
||||||
xyCoords
|
|
||||||
->E.A2.fmap(xyCoord =>
|
|
||||||
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
|
||||||
)
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
)
|
|
||||||
let expressionValue =
|
|
||||||
xyCoords
|
|
||||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
|
||||||
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
|
||||||
PointSet(makeDist(r)),
|
|
||||||
))
|
|
||||||
expressionValue
|
|
||||||
}
|
|
||||||
|
|
||||||
let registryStart = [
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Normal",
|
|
||||||
~examples=`normal(5,1)
|
|
||||||
normal({p5: 4, p95: 10})
|
|
||||||
normal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("normal", r =>
|
|
||||||
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Lognormal",
|
|
||||||
~examples=`lognormal(0.5, 0.8)
|
|
||||||
lognormal({p5: 4, p95: 10})
|
|
||||||
lognormal({mean: 5, stdev: 2})`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
|
||||||
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
|
||||||
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
|
||||||
),
|
|
||||||
TwoArgDist.makeRecordMeanStdev("lognormal", twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev)),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Uniform",
|
|
||||||
~examples=`uniform(10, 12)`,
|
|
||||||
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Beta",
|
|
||||||
~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(
|
|
||||||
~name="Cauchy",
|
|
||||||
~examples=`cauchy(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Gamma",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Logistic",
|
|
||||||
~examples=`gamma(5, 1)`,
|
|
||||||
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="To (Distribution)",
|
|
||||||
~examples=`5 to 10
|
|
||||||
to(5,10)
|
|
||||||
-5 to 5`,
|
|
||||||
~definitions=[
|
|
||||||
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
|
||||||
TwoArgDist.make(
|
|
||||||
"credibleIntervalToDistribution",
|
|
||||||
twoArgs(SymbolicDist.From90thPercentile.make),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Exponential",
|
|
||||||
~examples=`exponential(2)`,
|
|
||||||
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Bernoulli",
|
|
||||||
~examples=`bernoulli(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="PointMass",
|
|
||||||
~examples=`pointMass(0.5)`,
|
|
||||||
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~description="Converts a set of points to a continuous distribution",
|
|
||||||
~examples=`toContinuousPointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toContinuousPointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~description="Converts a set of points to a discrete distribution",
|
|
||||||
~examples=`toDiscretePointSet([
|
|
||||||
{x: 0, y: 0.1},
|
|
||||||
{x: 1, y: 0.2},
|
|
||||||
{x: 2, y: 0.15},
|
|
||||||
{x: 3, y: 0.1}
|
|
||||||
])`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="toDiscretePointSet",
|
|
||||||
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
|
||||||
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Declaration (Continuous Function)",
|
|
||||||
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
|
||||||
~examples=`declareFn({
|
|
||||||
fn: {|a,b| a },
|
|
||||||
inputs: [
|
|
||||||
{min: 0, max: 100},
|
|
||||||
{min: 30, max: 50}
|
|
||||||
]
|
|
||||||
})`,
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
|
|
||||||
inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
~isExperimental=true,
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Floor",
|
|
||||||
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Ceiling",
|
|
||||||
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Absolute Value",
|
|
||||||
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Exponent", ~definitions=[NumberToNumber.make("exp", Js.Math.exp)], ()),
|
|
||||||
Function.make(~name="Log", ~definitions=[NumberToNumber.make("log", Js.Math.log)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Log Base 10",
|
|
||||||
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(~name="Log Base 2", ~definitions=[NumberToNumber.make("log2", Js.Math.log2)], ()),
|
|
||||||
Function.make(~name="Round", ~definitions=[NumberToNumber.make("round", Js.Math.round)], ()),
|
|
||||||
Function.make(
|
|
||||||
~name="Sum",
|
|
||||||
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Product",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Min",
|
|
||||||
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Max",
|
|
||||||
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Mean",
|
|
||||||
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Geometric Mean",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Standard Deviation",
|
|
||||||
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Variance",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("variance", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="First",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("first", r =>
|
|
||||||
r->E.A.first |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Last",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make2("last", r =>
|
|
||||||
r->E.A.last |> E.O.toResult(impossibleError) |> E.R.fmap(Wrappers.evNumber)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Sort",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("sort", r =>
|
|
||||||
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Reverse",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("reverse", r =>
|
|
||||||
r->Belt_Array.reverse->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Sum",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumsum", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Cumulative Prod",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("cumprod", r =>
|
|
||||||
r->E.A.Floats.cumsum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Diff",
|
|
||||||
~definitions=[
|
|
||||||
ArrayNumberDist.make("diff", r =>
|
|
||||||
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.merge",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="merge",
|
|
||||||
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
|
||||||
~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1), FRValueDict(d2)] => {
|
|
||||||
let newDict =
|
|
||||||
E.Dict.concat(d1, d2) |> Js.Dict.map((. r) =>
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(r)
|
|
||||||
)
|
|
||||||
newDict->Js.Dict.entries->Belt.Map.String.fromArray->Wrappers.evRecord->Ok
|
|
||||||
}
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
//TODO: Make sure that two functions can't have the same name. This causes chaos elsewhere.
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.mergeMany",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="mergeMany", ~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToTypedArray.dicts
|
|
||||||
->E.R2.fmap(E.Dict.concatMany)
|
|
||||||
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
|
||||||
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.keys",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="keys", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] => Js.Dict.keys(d1)->E.A2.fmap(Wrappers.evString)->Wrappers.evArray->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.values",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="values", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(d1)] =>
|
|
||||||
Js.Dict.values(d1)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.toList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictToList", ~inputs=[FRTypeDict(FRTypeAny)], ~run=(inputs, _) =>
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDict(dict)] =>
|
|
||||||
dict
|
|
||||||
->Js.Dict.entries
|
|
||||||
->E.A2.fmap(((key, value)) =>
|
|
||||||
Wrappers.evArray([
|
|
||||||
Wrappers.evString(key),
|
|
||||||
FunctionRegistry_Core.FRType.matchReverse(value),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="Dict.fromList",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="dictFromList", ~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))], ~run=(
|
|
||||||
inputs,
|
|
||||||
_,
|
|
||||||
) => {
|
|
||||||
let convertInternalItems = items =>
|
|
||||||
items
|
|
||||||
->E.A2.fmap(item => {
|
|
||||||
switch item {
|
|
||||||
| [FRValueString(string), value] =>
|
|
||||||
(string, FunctionRegistry_Core.FRType.matchReverse(value))->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->E.A.R.firstErrorOrOpen
|
|
||||||
->E.R2.fmap(Belt.Map.String.fromArray)
|
|
||||||
->E.R2.fmap(Wrappers.evRecord)
|
|
||||||
inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.arrayOfArrays)
|
|
||||||
|> E.R2.bind(convertInternalItems)
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="List.make",
|
|
||||||
~definitions=[
|
|
||||||
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
|
||||||
FnDefinition.make(~name="listMake", ~inputs=[FRTypeNumber, FRTypeAny], ~run=(inputs, _) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueNumber(number), value] =>
|
|
||||||
Belt.Array.make(E.Float.toInt(number), value)
|
|
||||||
->E.A2.fmap(FunctionRegistry_Core.FRType.matchReverse)
|
|
||||||
->Wrappers.evArray
|
|
||||||
->Ok
|
|
||||||
| _ => Error(impossibleError)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
Function.make(
|
|
||||||
~name="upTo",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(~name="upTo", ~inputs=[FRTypeNumber, FRTypeNumber], ~run=(inputs, _) =>
|
|
||||||
inputs
|
|
||||||
->Prepare.ToValueTuple.twoNumbers
|
|
||||||
->E.R2.fmap(((low, high)) =>
|
|
||||||
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)
|
|
||||||
->E.A2.fmap(Wrappers.evNumber)
|
|
||||||
->Wrappers.evArray
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
let runScoring = (estimate, answer, prior, env) => {
|
|
||||||
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
|
|
||||||
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
|
|
||||||
->E.R2.errMap(DistributionTypes.Error.toString)
|
|
||||||
}
|
|
||||||
|
|
||||||
let scoreFunctions = [
|
|
||||||
Function.make(
|
|
||||||
~name="Score",
|
|
||||||
~definitions=[
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="logScore",
|
|
||||||
~inputs=[
|
|
||||||
FRTypeRecord([
|
|
||||||
("estimate", FRTypeDist),
|
|
||||||
("answer", FRTypeDistOrNumber),
|
|
||||||
("prior", FRTypeDist),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
~run=(inputs, env) => {
|
|
||||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
|
|
||||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
|
|
||||||
runScoring(estimate, Score_Dist(d), Some(prior), env)
|
|
||||||
| Ok([
|
|
||||||
FRValueDist(estimate),
|
|
||||||
FRValueDistOrNumber(FRValueNumber(d)),
|
|
||||||
FRValueDist(prior),
|
|
||||||
]) =>
|
|
||||||
runScoring(estimate, Score_Scalar(d), Some(prior), env)
|
|
||||||
| Error(e) => Error(e)
|
|
||||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
FnDefinition.make(
|
|
||||||
~name="logScore",
|
|
||||||
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
|
||||||
~run=(inputs, env) => {
|
|
||||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
|
|
||||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
|
|
||||||
runScoring(estimate, Score_Dist(d), None, env)
|
|
||||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
|
|
||||||
runScoring(estimate, Score_Scalar(d), None, env)
|
|
||||||
| Error(e) => Error(e)
|
|
||||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
FnDefinition.make(~name="klDivergence", ~inputs=[FRTypeDist, FRTypeDist], ~run=(
|
|
||||||
inputs,
|
|
||||||
env,
|
|
||||||
) => {
|
|
||||||
switch inputs {
|
|
||||||
| [FRValueDist(estimate), FRValueDist(d)] => runScoring(estimate, Score_Dist(d), None, env)
|
|
||||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
let registry = E.A.append(registryStart, scoreFunctions)
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Dict"
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
type t = ReducerInterface_InternalExpressionValue.map
|
||||||
|
|
||||||
|
let keys = (a: t): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString),
|
||||||
|
)
|
||||||
|
|
||||||
|
let values = (a: t): internalExpressionValue => IEvArray(Belt.Map.String.valuesToArray(a))
|
||||||
|
|
||||||
|
let toList = (a: t): internalExpressionValue =>
|
||||||
|
Belt.Map.String.toArray(a)
|
||||||
|
->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value]))
|
||||||
|
->Wrappers.evArray
|
||||||
|
|
||||||
|
let fromList = (items: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
items
|
||||||
|
->E.A2.fmap(item => {
|
||||||
|
switch (item: internalExpressionValue) {
|
||||||
|
| IEvArray([IEvString(string), value]) => (string, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
->E.R2.fmap(Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord)
|
||||||
|
|
||||||
|
let merge = (a: t, b: t): internalExpressionValue => IEvRecord(
|
||||||
|
Belt.Map.String.merge(a, b, (_, _, c) => c),
|
||||||
|
)
|
||||||
|
|
||||||
|
//Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet.
|
||||||
|
let mergeMany = (a: array<t>): internalExpressionValue => {
|
||||||
|
let mergedValues =
|
||||||
|
a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray
|
||||||
|
IEvRecord(mergedValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="merge",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.merge({a: 1, b: 2}, {c: 3, d: 4})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="merge",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1), IEvRecord(d2)] => Internals.merge(d1, d2)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
//TODO: Change to use new mergeMany() function.
|
||||||
|
Function.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="mergeMany",
|
||||||
|
~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToTypedArray.dicts
|
||||||
|
->E.R2.fmap(E.Dict.concatMany)
|
||||||
|
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r)))
|
||||||
|
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray)
|
||||||
|
->E.R2.fmap(Wrappers.evRecord),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="keys",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.keys({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="keys",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.keys(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="values",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.values({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="values",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(d1)] => Internals.values(d1)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="toList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`Dict.toList({a: 1, b: 2})`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="toList",
|
||||||
|
~inputs=[FRTypeDict(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvRecord(dict)] => dict->Internals.toList->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="fromList",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtRecord,
|
||||||
|
~examples=[`Dict.fromList([["a", 1], ["b", 2]])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="fromList",
|
||||||
|
~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(items)] => Internals.fromList(items)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,152 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
let twoArgs = E.Tuple2.toFnCall
|
||||||
|
|
||||||
|
module DistributionCreation = {
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let output = ReducerInterface_InternalExpressionValue.EvtDistribution
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
let fnMake = (~name, ~examples, ~definitions) => {
|
||||||
|
Function.make(~name, ~nameSpace, ~output, ~examples, ~definitions, ~requiresNamespace, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
module TwoArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordP5P95 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeRecordMeanStdev = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) =>
|
||||||
|
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module OneArgDist = {
|
||||||
|
let process = (~fn, ~env, r) =>
|
||||||
|
r
|
||||||
|
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
||||||
|
->E.R2.fmap(Wrappers.evDistribution)
|
||||||
|
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeDistOrNumber],
|
||||||
|
~run=(_, inputs, env) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
fnMake(
|
||||||
|
~name="normal",
|
||||||
|
~examples=["normal(5,1)", "normal({p5: 4, p95: 10})", "normal({mean: 5, stdev: 2})"],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("normal", r =>
|
||||||
|
twoArgs(SymbolicDist.Normal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev("normal", twoArgs(SymbolicDist.Normal.make)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="lognormal",
|
||||||
|
~examples=[
|
||||||
|
"lognormal(0.5, 0.8)",
|
||||||
|
"lognormal({p5: 4, p95: 10})",
|
||||||
|
"lognormal({mean: 5, stdev: 2})",
|
||||||
|
],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("lognormal", twoArgs(SymbolicDist.Lognormal.make)),
|
||||||
|
TwoArgDist.makeRecordP5P95("lognormal", r =>
|
||||||
|
twoArgs(SymbolicDist.Lognormal.from90PercentCI, r)->Ok
|
||||||
|
),
|
||||||
|
TwoArgDist.makeRecordMeanStdev(
|
||||||
|
"lognormal",
|
||||||
|
twoArgs(SymbolicDist.Lognormal.fromMeanAndStdev),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="uniform",
|
||||||
|
~examples=[`uniform(10, 12)`],
|
||||||
|
~definitions=[TwoArgDist.make("uniform", twoArgs(SymbolicDist.Uniform.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="beta",
|
||||||
|
~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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="cauchy",
|
||||||
|
~examples=[`cauchy(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("cauchy", twoArgs(SymbolicDist.Cauchy.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="gamma",
|
||||||
|
~examples=[`gamma(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("gamma", twoArgs(SymbolicDist.Gamma.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="logistic",
|
||||||
|
~examples=[`logistic(5, 1)`],
|
||||||
|
~definitions=[TwoArgDist.make("logistic", twoArgs(SymbolicDist.Logistic.make))],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="to (distribution)",
|
||||||
|
~examples=[`5 to 10`, `to(5,10)`, `-5 to 5`],
|
||||||
|
~definitions=[
|
||||||
|
TwoArgDist.make("to", twoArgs(SymbolicDist.From90thPercentile.make)),
|
||||||
|
TwoArgDist.make(
|
||||||
|
"credibleIntervalToDistribution",
|
||||||
|
twoArgs(SymbolicDist.From90thPercentile.make),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="exponential",
|
||||||
|
~examples=[`exponential(2)`],
|
||||||
|
~definitions=[OneArgDist.make("exponential", SymbolicDist.Exponential.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="bernoulli",
|
||||||
|
~examples=[`bernoulli(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("bernoulli", SymbolicDist.Bernoulli.make)],
|
||||||
|
),
|
||||||
|
fnMake(
|
||||||
|
~name="pointMass",
|
||||||
|
~examples=[`pointMass(0.5)`],
|
||||||
|
~definitions=[OneArgDist.make("pointMass", SymbolicDist.Float.makeSafe)],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = DistributionCreation.library
|
|
@ -0,0 +1,62 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
module Declaration = {
|
||||||
|
let frType = FRTypeRecord([
|
||||||
|
("fn", FRTypeLambda),
|
||||||
|
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
|
||||||
|
])
|
||||||
|
|
||||||
|
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => {
|
||||||
|
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_InternalExpressionValue.IEvDeclaration(
|
||||||
|
Declaration.make(lambda, args),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
| Error(r) => Error(r)
|
||||||
|
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nameSpace = "Function"
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="declare",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace=true,
|
||||||
|
~output=EvtDeclaration,
|
||||||
|
~description="Adds metadata to a function of the input ranges. Works now for numeric and date inputs. This is useful when making predictions. It allows you to limit the domain that your prediction will be used and scored within.",
|
||||||
|
~examples=[
|
||||||
|
`Function.declare({
|
||||||
|
fn: {|a,b| a },
|
||||||
|
inputs: [
|
||||||
|
{min: 0, max: 100},
|
||||||
|
{min: 30, max: 50}
|
||||||
|
]
|
||||||
|
})`,
|
||||||
|
],
|
||||||
|
~isExperimental=true,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="declare",
|
||||||
|
~inputs=[Declaration.frType],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,128 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "List"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
module Internals = {
|
||||||
|
let makeFromNumber = (
|
||||||
|
n: float,
|
||||||
|
value: internalExpressionValue,
|
||||||
|
): internalExpressionValue => IEvArray(Belt.Array.make(E.Float.toInt(n), value))
|
||||||
|
|
||||||
|
let upTo = (low: float, high: float): internalExpressionValue => IEvArray(
|
||||||
|
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber),
|
||||||
|
)
|
||||||
|
|
||||||
|
let first = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.first |> E.O.toResult("No first element")
|
||||||
|
|
||||||
|
let last = (v: array<internalExpressionValue>): result<internalExpressionValue, string> =>
|
||||||
|
v->E.A.last |> E.O.toResult("No last element")
|
||||||
|
|
||||||
|
let reverse = (array: array<internalExpressionValue>): internalExpressionValue => IEvArray(
|
||||||
|
Belt.Array.reverse(array),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="make",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.make(2, "testValue")`],
|
||||||
|
~definitions=[
|
||||||
|
//Todo: If the second item is a function with no args, it could be nice to run this function and return the result.
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="make",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeAny],
|
||||||
|
~run=(inputs, _, _) => {
|
||||||
|
switch inputs {
|
||||||
|
| [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="upTo",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`List.upTo(1,4)`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="upTo",
|
||||||
|
~inputs=[FRTypeNumber, FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
inputs
|
||||||
|
->Prepare.ToValueTuple.twoNumbers
|
||||||
|
->E.R2.fmap(((low, high)) => Internals.upTo(low, high)),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="first",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.first([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="first",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.first(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="last",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[`List.last([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="last",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.last(array)
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="reverse",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~requiresNamespace=false,
|
||||||
|
~examples=[`List.reverse([1,4,5])`],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="reverse",
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(inputs, _, _) =>
|
||||||
|
switch inputs {
|
||||||
|
| [IEvArray(array)] => Internals.reverse(array)->Ok
|
||||||
|
| _ => Error(impossibleError)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,251 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Number"
|
||||||
|
let requiresNamespace = false
|
||||||
|
|
||||||
|
module NumberToNumber = {
|
||||||
|
let make = (name, fn) =>
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeNumber],
|
||||||
|
~run=(_, inputs, _) => {
|
||||||
|
inputs
|
||||||
|
->getOrError(0)
|
||||||
|
->E.R.bind(Prepare.oneNumber)
|
||||||
|
->E.R2.fmap(fn)
|
||||||
|
->E.R2.fmap(Wrappers.evNumber)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module ArrayNumberDist = {
|
||||||
|
let make = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeNumber)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let make2 = (name, fn) => {
|
||||||
|
FnDefinition.make(
|
||||||
|
~name,
|
||||||
|
~inputs=[FRTypeArray(FRTypeAny)],
|
||||||
|
~run=(_, inputs, _) =>
|
||||||
|
Prepare.ToTypedArray.numbers(inputs)
|
||||||
|
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
|
||||||
|
->E.R.bind(fn),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="floor",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`floor(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("floor", Js.Math.floor_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="ceiling",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`ceil(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("ceil", Js.Math.ceil_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="absolute value",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`abs(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("abs", Js.Math.abs_float)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="exponent",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`exp(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("exp", Js.Math.exp)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log", Js.Math.log)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 10",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log10(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log10", Js.Math.log10)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="log base 2",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`log2(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("log2", Js.Math.log2)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="round",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`round(3.5)`],
|
||||||
|
~definitions=[NumberToNumber.make("round", Js.Math.round)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`sum([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("sum", r => r->E.A.Floats.sum->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="product",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`product([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("product", r => r->E.A.Floats.product->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="min",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`min([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("min", r => r->E.A.Floats.min->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="max",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`max([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("max", r => r->E.A.Floats.max->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`mean([3,5,2])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("mean", r => r->E.A.Floats.mean->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="geometric mean",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`geomean([3,5,2])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("geomean", r => r->E.A.Floats.geomean->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="standard deviation",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`stdev([3,5,2,3,5])`],
|
||||||
|
~definitions=[ArrayNumberDist.make("stdev", r => r->E.A.Floats.stdev->Wrappers.evNumber->Ok)],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="variance",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[`variance([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("variance", r => r->E.A.Floats.variance->Wrappers.evNumber->Ok),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="sort",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`sort([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("sort", r =>
|
||||||
|
r->E.A.Floats.sort->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative sum",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumsum([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumsum", r =>
|
||||||
|
r->E.A.Floats.cumSum->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="cumulative prod",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`cumprod([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("cumprod", r =>
|
||||||
|
r->E.A.Floats.cumProd->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="diff",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtArray,
|
||||||
|
~examples=[`diff([3,5,2,3,5])`],
|
||||||
|
~definitions=[
|
||||||
|
ArrayNumberDist.make("diff", r =>
|
||||||
|
r->E.A.Floats.diff->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,73 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
open FunctionRegistry_Helpers
|
||||||
|
|
||||||
|
let nameSpace = "Pointset"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
|
||||||
|
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA)
|
||||||
|
let xyCoords =
|
||||||
|
array->E.R.bind(xyCoords =>
|
||||||
|
xyCoords
|
||||||
|
->E.A2.fmap(xyCoord =>
|
||||||
|
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
|
||||||
|
)
|
||||||
|
->E.A.R.firstErrorOrOpen
|
||||||
|
)
|
||||||
|
let expressionValue =
|
||||||
|
xyCoords
|
||||||
|
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||||
|
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution(
|
||||||
|
PointSet(makeDist(r)),
|
||||||
|
))
|
||||||
|
expressionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeContinuous([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeContinuous",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=[
|
||||||
|
`Pointset.makeDiscrete([
|
||||||
|
{x: 0, y: 0.2},
|
||||||
|
{x: 1, y: 0.7},
|
||||||
|
{x: 2, y: 0.8},
|
||||||
|
{x: 3, y: 0.2}
|
||||||
|
])`,
|
||||||
|
],
|
||||||
|
~output=ReducerInterface_InternalExpressionValue.EvtDistribution,
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="makeDiscrete",
|
||||||
|
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
|
||||||
|
~run=(_, inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,89 @@
|
||||||
|
open FunctionRegistry_Core
|
||||||
|
|
||||||
|
let nameSpace = "Dist"
|
||||||
|
let requiresNamespace = true
|
||||||
|
|
||||||
|
let runScoring = (estimate, answer, prior, env) => {
|
||||||
|
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
|
||||||
|
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
|
||||||
|
->E.R2.errMap(DistributionTypes.Error.toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
let library = [
|
||||||
|
Function.make(
|
||||||
|
~name="logScore",
|
||||||
|
~nameSpace,
|
||||||
|
~requiresNamespace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~examples=[
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1), prior: normal(5.5,3)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: normal(5.2,1)})",
|
||||||
|
"Dist.logScore({estimate: normal(5,2), answer: 4.5})",
|
||||||
|
],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="logScore",
|
||||||
|
~inputs=[
|
||||||
|
FRTypeRecord([
|
||||||
|
("estimate", FRTypeDist),
|
||||||
|
("answer", FRTypeDistOrNumber),
|
||||||
|
("prior", FRTypeDist),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
|
||||||
|
runScoring(estimate, Score_Dist(d), Some(prior), env)
|
||||||
|
| Ok([
|
||||||
|
FRValueDist(estimate),
|
||||||
|
FRValueDistOrNumber(FRValueNumber(d)),
|
||||||
|
FRValueDist(prior),
|
||||||
|
]) =>
|
||||||
|
runScoring(estimate, Score_Scalar(d), Some(prior), env)
|
||||||
|
| Error(e) => Error(e)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="logScore",
|
||||||
|
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
|
||||||
|
runScoring(estimate, Score_Dist(d), None, env)
|
||||||
|
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
|
||||||
|
runScoring(estimate, Score_Scalar(d), None, env)
|
||||||
|
| Error(e) => Error(e)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
Function.make(
|
||||||
|
~name="klDivergence",
|
||||||
|
~nameSpace,
|
||||||
|
~output=EvtNumber,
|
||||||
|
~requiresNamespace,
|
||||||
|
~examples=["Dist.klDivergence(normal(5,2), normal(5,1.5))"],
|
||||||
|
~definitions=[
|
||||||
|
FnDefinition.make(
|
||||||
|
~name="klDivergence",
|
||||||
|
~inputs=[FRTypeDist, FRTypeDist],
|
||||||
|
~run=(_, inputs, env) => {
|
||||||
|
switch inputs {
|
||||||
|
| [FRValueDist(estimate), FRValueDist(d)] =>
|
||||||
|
runScoring(estimate, Score_Dist(d), None, env)
|
||||||
|
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
(),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Only Bindings as the global module is supported
|
||||||
|
// Other module operations such as import export will be prepreocessed jobs
|
||||||
|
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
open Reducer_ErrorValue
|
open Reducer_ErrorValue
|
||||||
|
|
|
@ -10,8 +10,8 @@ open ReducerInterface_InternalExpressionValue
|
||||||
open Reducer_ErrorValue
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MathJs provides default implementations for builtins
|
MathJs provides default implementations for built-ins
|
||||||
This is where all the expected builtins like + = * / sin cos log ln etc are handled
|
This is where all the expected built-ins like + = * / sin cos log ln etc are handled
|
||||||
DO NOT try to add external function mapping here!
|
DO NOT try to add external function mapping here!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -149,6 +149,27 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)})
|
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)})
|
||||||
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parseSampleSetArray = (arr: array<internalExpressionValue>): option<
|
||||||
|
array<SampleSetDist.t>,
|
||||||
|
> => {
|
||||||
|
let parseSampleSet = (value: internalExpressionValue): option<SampleSetDist.t> =>
|
||||||
|
switch value {
|
||||||
|
| IEvDistribution(SampleSet(dist)) => Some(dist)
|
||||||
|
| _ => None
|
||||||
|
}
|
||||||
|
E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapN = (aValueArray: array<internalExpressionValue>, aLambdaValue) => {
|
||||||
|
switch parseSampleSetArray(aValueArray) {
|
||||||
|
| Some(t1) =>
|
||||||
|
let fn = a => doLambdaCall(aLambdaValue, list{IEvArray(E.A.fmap(x => IEvNumber(x), a))})
|
||||||
|
SampleSetDist.mapN(~fn, ~t1)->toType
|
||||||
|
| None =>
|
||||||
|
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let doReduceArray = (aValueArray, initialValue, aLambdaValue) => {
|
let doReduceArray = (aValueArray, initialValue, aLambdaValue) => {
|
||||||
|
@ -198,7 +219,7 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
| ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr)
|
| ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr)
|
||||||
| ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems)
|
| ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems)
|
||||||
| ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem)
|
| ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem)
|
||||||
| ("$_typeRecord_$", [IEvArray(arrayOfPairs)]) => TypeBuilder.typeRecord(arrayOfPairs)
|
| ("$_typeRecord_$", [IEvRecord(propertyMap)]) => TypeBuilder.typeRecord(propertyMap)
|
||||||
| ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) =>
|
| ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) =>
|
||||||
doAddArray(aValueArray, bValueArray)
|
doAddArray(aValueArray, bValueArray)
|
||||||
| ("concat", [IEvString(aValueString), IEvString(bValueString)]) =>
|
| ("concat", [IEvString(aValueString), IEvString(bValueString)]) =>
|
||||||
|
@ -230,6 +251,8 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
],
|
],
|
||||||
) =>
|
) =>
|
||||||
SampleMap.map3(dist1, dist2, dist3, aLambdaValue)
|
SampleMap.map3(dist1, dist2, dist3, aLambdaValue)
|
||||||
|
| ("mapSamplesN", [IEvArray(aValueArray), IEvLambda(aLambdaValue)]) =>
|
||||||
|
SampleMap.mapN(aValueArray, aLambdaValue)
|
||||||
| ("reduce", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
| ("reduce", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
||||||
doReduceArray(aValueArray, initialValue, aLambdaValue)
|
doReduceArray(aValueArray, initialValue, aLambdaValue)
|
||||||
| ("reduceReverse", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
| ("reduceReverse", [IEvArray(aValueArray), initialValue, IEvLambda(aLambdaValue)]) =>
|
||||||
|
@ -246,7 +269,6 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error
|
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reducer uses Result monad while reducing expressions
|
Reducer uses Result monad while reducing expressions
|
||||||
*/
|
*/
|
||||||
|
@ -255,11 +277,10 @@ let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn)
|
||||||
errorValue,
|
errorValue,
|
||||||
> =>
|
> =>
|
||||||
try {
|
try {
|
||||||
let callInternalWithReducer = (call, environment) => callInternal(call, environment, reducer)
|
|
||||||
let (fn, args) = call
|
let (fn, args) = call
|
||||||
// There is a bug that prevents string match in patterns
|
// There is a bug that prevents string match in patterns
|
||||||
// So we have to recreate a copy of the string
|
// So we have to recreate a copy of the string
|
||||||
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternalWithReducer)
|
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, reducer, callInternal)
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
| _ => RETodo("unhandled rescript exception")->Error
|
| _ => RETodo("unhandled rescript exception")->Error
|
||||||
|
|
|
@ -144,7 +144,7 @@ let dispatchMacroCall = (
|
||||||
let ifTrueBlock = eBlock(list{ifTrue})
|
let ifTrueBlock = eBlock(list{ifTrue})
|
||||||
ExpressionWithContext.withContext(ifTrueBlock, bindings)->Ok
|
ExpressionWithContext.withContext(ifTrueBlock, bindings)->Ok
|
||||||
}
|
}
|
||||||
| _ => REExpectedType("Boolean")->Error
|
| _ => REExpectedType("Boolean", "")->Error
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ type errorValue =
|
||||||
| REArrayIndexNotFound(string, int)
|
| REArrayIndexNotFound(string, int)
|
||||||
| REAssignmentExpected
|
| REAssignmentExpected
|
||||||
| REDistributionError(DistributionTypes.error)
|
| REDistributionError(DistributionTypes.error)
|
||||||
| REExpectedType(string)
|
| REExpectedType(string, string)
|
||||||
| REExpressionExpected
|
| REExpressionExpected
|
||||||
| REFunctionExpected(string)
|
| REFunctionExpected(string)
|
||||||
| REFunctionNotFound(string)
|
| REFunctionNotFound(string)
|
||||||
|
@ -55,6 +55,6 @@ let errorToString = err =>
|
||||||
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
||||||
| RESyntaxError(desc, _) => `Syntax Error: ${desc}`
|
| RESyntaxError(desc, _) => `Syntax Error: ${desc}`
|
||||||
| RETodo(msg) => `TODO: ${msg}`
|
| RETodo(msg) => `TODO: ${msg}`
|
||||||
| REExpectedType(typeName) => `Expected type: ${typeName}`
|
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
|
||||||
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
// There are switch stament cases in the code which are impossible to reach by design.
|
// There are switch statement cases in the code which are impossible to reach by design.
|
||||||
// ImpossibleException is a sign of programming error.
|
// ImpossibleException is a sign of programming error.
|
||||||
exception ImpossibleException
|
exception ImpossibleException(string)
|
||||||
|
|
|
@ -30,12 +30,12 @@ let rec toString = expression =>
|
||||||
switch expression {
|
switch expression {
|
||||||
| EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) =>
|
| EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) =>
|
||||||
`{${Belt.List.map(statements, aValue => toString(aValue))
|
`{${Belt.List.map(statements, aValue => toString(aValue))
|
||||||
->Extra.List.interperse("; ")
|
->Extra.List.intersperse("; ")
|
||||||
->Belt.List.toArray
|
->Belt.List.toArray
|
||||||
->Js.String.concatMany("")}}`
|
->Js.String.concatMany("")}}`
|
||||||
| EList(aList) =>
|
| EList(aList) =>
|
||||||
`(${Belt.List.map(aList, aValue => toString(aValue))
|
`(${Belt.List.map(aList, aValue => toString(aValue))
|
||||||
->Extra.List.interperse(" ")
|
->Extra.List.intersperse(" ")
|
||||||
->Belt.List.toArray
|
->Belt.List.toArray
|
||||||
->Js.String.concatMany("")})`
|
->Js.String.concatMany("")})`
|
||||||
| EValue(aValue) => InternalExpressionValue.toString(aValue)
|
| EValue(aValue) => InternalExpressionValue.toString(aValue)
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
*/
|
*/
|
||||||
module ExtraList = Reducer_Extra_List
|
module ExtraList = Reducer_Extra_List
|
||||||
|
|
||||||
let interperse = (anArray, seperator) =>
|
let intersperse = (anArray, seperator) =>
|
||||||
anArray->Belt.List.fromArray->ExtraList.interperse(seperator)->Belt.List.toArray
|
anArray->Belt.List.fromArray->ExtraList.intersperse(seperator)->Belt.List.toArray
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
Insert seperator between the elements of a list
|
Insert seperator between the elements of a list
|
||||||
*/
|
*/
|
||||||
let rec interperse = (aList, seperator) =>
|
let rec intersperse = (aList, seperator) =>
|
||||||
switch aList {
|
switch aList {
|
||||||
| list{} => list{}
|
| list{} => list{}
|
||||||
| list{a} => list{a}
|
| list{a} => list{a}
|
||||||
| list{a, ...rest} => list{a, seperator, ...interperse(rest, seperator)}
|
| list{a, ...rest} => list{a, seperator, ...intersperse(rest, seperator)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ let rec pgToString = (peggyNode: peggyNode): string => {
|
||||||
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString
|
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString
|
||||||
|
|
||||||
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
|
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
|
||||||
nodes->Js.Array2.map(toString)->Extra.Array.interperse(separator)->Js.String.concatMany("")
|
nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||||
|
|
||||||
switch peggyNode {
|
switch peggyNode {
|
||||||
| PgNodeBlock(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
|
| PgNodeBlock(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module Bindings = Reducer_Bindings
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
|
||||||
|
let ievFromTypeExpression = (
|
||||||
|
typeExpressionSourceCode: string,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
|
let sIndex = "compiled"
|
||||||
|
let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}`
|
||||||
|
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr => {
|
||||||
|
let rContext = reducerFn(
|
||||||
|
expr,
|
||||||
|
Bindings.emptyBindings,
|
||||||
|
InternalExpressionValue.defaultEnvironment,
|
||||||
|
)
|
||||||
|
Belt.Result.map(rContext, context =>
|
||||||
|
switch context {
|
||||||
|
| IEvBindings(nameSpace) =>
|
||||||
|
switch Bindings.getType(nameSpace, sIndex) {
|
||||||
|
| Some(value) => value
|
||||||
|
| None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
|
||||||
|
}
|
||||||
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-raise"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromTypeExpression = (
|
||||||
|
typeExpressionSourceCode: string,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<T.t, ErrorValue.t> => {
|
||||||
|
ievFromTypeExpression(
|
||||||
|
(typeExpressionSourceCode: string),
|
||||||
|
(reducerFn: ExpressionT.reducerFn),
|
||||||
|
)->Belt.Result.map(T.fromIEvValue)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
module T = Reducer_Type_T
|
||||||
|
|
||||||
|
let isMin = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (IEvNumber(a), IEvNumber(b)) => a <= b
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isMax = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (IEvNumber(a), IEvNumber(b)) => a >= b
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isMemberOf = (
|
||||||
|
modifierArg: InternalExpressionValue.t,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool => {
|
||||||
|
let pair = (modifierArg, aValue)
|
||||||
|
switch pair {
|
||||||
|
| (ievA, IEvArray(b)) => Js.Array2.includes(b, ievA)
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkModifier = (
|
||||||
|
key: string,
|
||||||
|
modifierArg: InternalExpressionValue.t,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool =>
|
||||||
|
switch key {
|
||||||
|
| "min" => isMin(modifierArg, aValue)
|
||||||
|
| "max" => isMax(modifierArg, aValue)
|
||||||
|
| "isMemberOf" => isMemberOf(modifierArg, aValue)
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkModifiers = (
|
||||||
|
contracts: Belt.Map.String.t<InternalExpressionValue.t>,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
): bool => {
|
||||||
|
contracts->Belt.Map.String.reduce(true, (acc, key, modifierArg) =>
|
||||||
|
switch acc {
|
||||||
|
| true => checkModifier(key, modifierArg, aValue)
|
||||||
|
| _ => acc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -3,13 +3,42 @@ open InternalExpressionValue
|
||||||
|
|
||||||
type rec iType =
|
type rec iType =
|
||||||
| ItTypeIdentifier(string)
|
| ItTypeIdentifier(string)
|
||||||
| ItModifiedType({modifiedType: iType})
|
| ItModifiedType({modifiedType: iType, contracts: Belt.Map.String.t<InternalExpressionValue.t>})
|
||||||
| ItTypeOr({typeOr: array<iType>})
|
| ItTypeOr({typeOr: array<iType>})
|
||||||
| ItTypeFunction({inputs: array<iType>, output: iType})
|
| ItTypeFunction({inputs: array<iType>, output: iType})
|
||||||
| ItTypeArray({element: iType})
|
| ItTypeArray({element: iType})
|
||||||
| ItTypeTuple({elements: array<iType>})
|
| ItTypeTuple({elements: array<iType>})
|
||||||
| ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
| ItTypeRecord({properties: Belt.Map.String.t<iType>})
|
||||||
|
|
||||||
|
type t = iType
|
||||||
|
type typeErrorValue = TypeMismatch(t, InternalExpressionValue.t)
|
||||||
|
|
||||||
|
let rec toString = (t: t): string => {
|
||||||
|
switch t {
|
||||||
|
| ItTypeIdentifier(s) => s
|
||||||
|
| ItModifiedType({modifiedType, contracts}) =>
|
||||||
|
`${toString(modifiedType)}${contracts->Belt.Map.String.reduce("", (acc, k, v) =>
|
||||||
|
Js.String2.concatMany(acc, ["<-", k, "(", InternalExpressionValue.toString(v), ")"])
|
||||||
|
)}`
|
||||||
|
| ItTypeOr({typeOr}) => `(${Js.Array2.map(typeOr, toString)->Js.Array2.joinWith(" | ")})`
|
||||||
|
| ItTypeFunction({inputs, output}) =>
|
||||||
|
`(${inputs->Js.Array2.map(toString)->Js.Array2.joinWith(" => ")} => ${toString(output)})`
|
||||||
|
| ItTypeArray({element}) => `[${toString(element)}]`
|
||||||
|
| ItTypeTuple({elements}) => `[${Js.Array2.map(elements, toString)->Js.Array2.joinWith(", ")}]`
|
||||||
|
| ItTypeRecord({properties}) =>
|
||||||
|
`{${properties
|
||||||
|
->Belt.Map.String.toArray
|
||||||
|
->Js.Array2.map(((k, v)) => Js.String2.concatMany(k, [": ", toString(v)]))
|
||||||
|
->Js.Array2.joinWith(", ")}}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toStringResult = (rt: result<t, ErrorValue.t>) =>
|
||||||
|
switch rt {
|
||||||
|
| Ok(t) => toString(t)
|
||||||
|
| Error(e) => ErrorValue.errorToString(e)
|
||||||
|
}
|
||||||
|
|
||||||
let rec fromTypeMap = typeMap => {
|
let rec fromTypeMap = typeMap => {
|
||||||
let default = IEvString("")
|
let default = IEvString("")
|
||||||
let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault(
|
let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault(
|
||||||
|
@ -52,31 +81,39 @@ let rec fromTypeMap = typeMap => {
|
||||||
"properties",
|
"properties",
|
||||||
default,
|
default,
|
||||||
)
|
)
|
||||||
//TODO: map type modifiers
|
|
||||||
switch evTypeTag {
|
let contracts =
|
||||||
| IEvString("typeIdentifier") => ItModifiedType({modifiedType: fromIEvValue(evTypeIdentifier)})
|
typeMap->Belt.Map.String.keep((k, _v) => ["min", "max", "memberOf"]->Js.Array2.includes(k))
|
||||||
|
|
||||||
|
let makeIt = switch evTypeTag {
|
||||||
|
| IEvString("typeIdentifier") => fromIEvValue(evTypeIdentifier)
|
||||||
| IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)})
|
| IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)})
|
||||||
| IEvString("typeFunction") =>
|
| IEvString("typeFunction") =>
|
||||||
ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)})
|
ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)})
|
||||||
| IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)})
|
| IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)})
|
||||||
| IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)})
|
| IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)})
|
||||||
| IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)})
|
| IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)})
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-evTypeTag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Belt.Map.String.isEmpty(contracts)
|
||||||
|
? makeIt
|
||||||
|
: ItModifiedType({modifiedType: makeIt, contracts: contracts})
|
||||||
}
|
}
|
||||||
and fromIEvValue = (ievValue: InternalExpressionValue.t) =>
|
|
||||||
|
and fromIEvValue = (ievValue: InternalExpressionValue.t): iType =>
|
||||||
switch ievValue {
|
switch ievValue {
|
||||||
| IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier})
|
| IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier})
|
||||||
| IEvType(typeMap) => fromTypeMap(typeMap)
|
| IEvType(typeMap) => fromTypeMap(typeMap)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievValue"))
|
||||||
}
|
}
|
||||||
and fromIEvArray = (ievArray: InternalExpressionValue.t) =>
|
and fromIEvArray = (ievArray: InternalExpressionValue.t) =>
|
||||||
switch ievArray {
|
switch ievArray {
|
||||||
| IEvArray(array) => array->Belt.Array.map(fromIEvValue)
|
| IEvArray(array) => array->Belt.Array.map(fromIEvValue)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievArray"))
|
||||||
}
|
}
|
||||||
and fromIEvRecord = (ievRecord: InternalExpressionValue.t) =>
|
and fromIEvRecord = (ievRecord: InternalExpressionValue.t) =>
|
||||||
switch ievRecord {
|
switch ievRecord {
|
||||||
| IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue)
|
| IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue)
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievRecord"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ let typeFunction = anArray => {
|
||||||
|
|
||||||
let typeArray = element => {
|
let typeArray = element => {
|
||||||
let newRecord = Belt.Map.String.fromArray([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeTuple")),
|
("typeTag", IEvString("typeArray")),
|
||||||
("element", element),
|
("element", element),
|
||||||
])
|
])
|
||||||
newRecord->IEvType->Ok
|
newRecord->IEvType->Ok
|
||||||
|
@ -64,22 +64,14 @@ let typeArray = element => {
|
||||||
|
|
||||||
let typeTuple = anArray => {
|
let typeTuple = anArray => {
|
||||||
let newRecord = Belt.Map.String.fromArray([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeArray")),
|
("typeTag", IEvString("typeTuple")),
|
||||||
("elements", IEvArray(anArray)),
|
("elements", IEvArray(anArray)),
|
||||||
])
|
])
|
||||||
newRecord->IEvType->Ok
|
newRecord->IEvType->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
let typeRecord = arrayOfPairs => {
|
let typeRecord = propertyMap => {
|
||||||
let newProperties =
|
let newProperties = propertyMap->IEvRecord
|
||||||
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([
|
let newRecord = Belt.Map.String.fromArray([
|
||||||
("typeTag", IEvString("typeRecord")),
|
("typeTag", IEvString("typeRecord")),
|
||||||
("properties", newProperties),
|
("properties", newProperties),
|
||||||
|
|
|
@ -1,81 +1,168 @@
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
module T = Reducer_Type_T
|
module T = Reducer_Type_T
|
||||||
module TypeBuilder = Reducer_Type_TypeBuilder
|
module TypeContracts = Reducer_Type_Contracts
|
||||||
open InternalExpressionValue
|
open InternalExpressionValue
|
||||||
|
|
||||||
type typeErrorValue =
|
let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.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<bool, typeErrorValue> => {
|
|
||||||
let caseTypeIdentifier = (anUpperTypeName, aValue) => {
|
let caseTypeIdentifier = (anUpperTypeName, aValue) => {
|
||||||
let aTypeName = anUpperTypeName->Js.String2.toLowerCase
|
let aTypeName = anUpperTypeName->Js.String2.toLowerCase
|
||||||
let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase
|
let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase
|
||||||
switch aTypeName === valueTypeName {
|
switch aTypeName == valueTypeName {
|
||||||
| true => Ok(true)
|
| true => Ok(true)
|
||||||
| false => TypeError(anIType, aValue)->Error
|
| false => T.TypeMismatch(anIType, aValue)->Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _caseRecord = (anIType, evValue, propertyMap, map) => {
|
let caseRecord = (anIType, propertyMap: Belt.Map.String.t<T.iType>, evValue) =>
|
||||||
Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => {
|
switch evValue {
|
||||||
|
| IEvRecord(aRecord) =>
|
||||||
|
if (
|
||||||
|
Js.Array2.length(propertyMap->Belt.Map.String.keysToArray) ==
|
||||||
|
Js.Array2.length(aRecord->Belt.Map.String.keysToArray)
|
||||||
|
) {
|
||||||
|
Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => {
|
||||||
|
Belt.Result.flatMap(acc, _ =>
|
||||||
|
switch Belt.Map.String.get(aRecord, property) {
|
||||||
|
| Some(propertyValue) => isITypeOf(propertyType, propertyValue)
|
||||||
|
| None => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseArray = (anIType, elementType, evValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| IEvArray(anArray) =>
|
||||||
|
Belt.Array.reduce(anArray, Ok(true), (acc, element) =>
|
||||||
|
Belt.Result.flatMap(acc, _ =>
|
||||||
|
switch isITypeOf(elementType, element) {
|
||||||
|
| Ok(_) => Ok(true)
|
||||||
|
| Error(error) => error->Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseTuple = (anIType, elementTypes, evValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| IEvArray(anArray) =>
|
||||||
|
if Js.Array2.length(elementTypes) == Js.Array2.length(anArray) {
|
||||||
|
let zipped = Belt.Array.zip(elementTypes, anArray)
|
||||||
|
Belt.Array.reduce(zipped, Ok(true), (acc, (elementType, element)) =>
|
||||||
|
switch acc {
|
||||||
|
| Ok(_) =>
|
||||||
|
switch isITypeOf(elementType, element) {
|
||||||
|
| Ok(_) => acc
|
||||||
|
| Error(error) => Error(error)
|
||||||
|
}
|
||||||
|
| _ => acc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
| _ => T.TypeMismatch(anIType, evValue)->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseOr = (anIType, anITypeArray, evValue) =>
|
||||||
|
switch Belt.Array.reduce(anITypeArray, Ok(false), (acc, anIType) =>
|
||||||
Belt.Result.flatMap(acc, _ =>
|
Belt.Result.flatMap(acc, _ =>
|
||||||
switch Belt.Map.String.get(map, property) {
|
switch acc {
|
||||||
| Some(propertyValue) => isOfResolvedIType(propertyType, propertyValue)
|
| Ok(false) =>
|
||||||
| None => TypeErrorWithProperty(anIType, evValue, property)->Error
|
switch isITypeOf(anIType, evValue) {
|
||||||
|
| Ok(_) => Ok(true)
|
||||||
|
| Error(_) => acc
|
||||||
|
}
|
||||||
|
| _ => acc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
) {
|
||||||
}
|
| Ok(true) => Ok(true)
|
||||||
let _caseArray = (anIType, evValue, elementType, anArray) => {
|
| Ok(false) => T.TypeMismatch(anIType, evValue)->Error
|
||||||
Belt.Array.reduceWithIndex(anArray, Ok(true), (acc, element, index) => {
|
| Error(error) => Error(error)
|
||||||
switch isOfResolvedIType(elementType, element) {
|
}
|
||||||
| Ok(_) => acc
|
|
||||||
| Error(_) => TypeErrorWithPosition(anIType, evValue, index)->Error
|
let caseModifiedType = (
|
||||||
|
anIType: T.iType,
|
||||||
|
modifiedType: T.iType,
|
||||||
|
contracts: Belt.Map.String.t<InternalExpressionValue.t>,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
) => {
|
||||||
|
isITypeOf(modifiedType, aValue)->Belt.Result.flatMap(_result => {
|
||||||
|
if TypeContracts.checkModifiers(contracts, aValue) {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
T.TypeMismatch(anIType, aValue)->Error
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
switch anIType {
|
switch anIType {
|
||||||
| ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue)
|
| ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue)
|
||||||
// TODO: Work in progress. Code is commented to make an a release of other features
|
| ItModifiedType({modifiedType, contracts}) =>
|
||||||
// | ItModifiedType({modifiedType: anIType}) => raise(Reducer_Exception.ImpossibleException)
|
caseModifiedType(anIType, modifiedType, contracts, aValue) //{modifiedType: iType, contracts: Belt.Map.String.t<InternalExpressionValue.t>}
|
||||||
// | ItTypeOr({typeOr: anITypeArray}) => raise(Reducer_Exception.ImpossibleException)
|
| ItTypeOr({typeOr}) => caseOr(anIType, typeOr, aValue)
|
||||||
// | ItTypeFunction({inputs: anITypeArray, output: anIType}) =>
|
| ItTypeFunction(_) =>
|
||||||
// raise(Reducer_Exception.ImpossibleException)
|
raise(
|
||||||
// | ItTypeArray({element: anIType}) => raise(Reducer_Exception.ImpossibleException)
|
Reducer_Exception.ImpossibleException(
|
||||||
// | ItTypeTuple({elements: anITypeArray}) => raise(Reducer_Exception.ImpossibleException)
|
"Reducer_TypeChecker-functions are without a type at the moment",
|
||||||
// | ItTypeRecord({properties: anITypeMap}) => raise(Reducer_Exception.ImpossibleException)
|
),
|
||||||
| _ => raise(Reducer_Exception.ImpossibleException)
|
)
|
||||||
|
| ItTypeArray({element}) => caseArray(anIType, element, aValue)
|
||||||
|
| ItTypeTuple({elements}) => caseTuple(anIType, elements, aValue)
|
||||||
|
| ItTypeRecord({properties}) => caseRecord(anIType, properties, aValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isOfResolvedType = (aType: InternalExpressionValue.t, aValue): result<bool, typeErrorValue> =>
|
let isTypeOf = (
|
||||||
aType->T.fromIEvValue->isOfResolvedIType(aValue)
|
typeExpressionSourceCode: string,
|
||||||
|
aValue: InternalExpressionValue.t,
|
||||||
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
|
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||||
|
| Ok(anIType) =>
|
||||||
|
switch isITypeOf(anIType, aValue) {
|
||||||
|
| Ok(_) => Ok(aValue)
|
||||||
|
| Error(T.TypeMismatch(anIType, evValue)) =>
|
||||||
|
Error(
|
||||||
|
ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
| Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Work in progress. Code is commented to make an a release of other features
|
let checkITypeArguments = (anIType: T.iType, args: array<InternalExpressionValue.t>): result<
|
||||||
// let checkArguments = (
|
bool,
|
||||||
// evFunctionType: InternalExpressionValue.t,
|
T.typeErrorValue,
|
||||||
// args: array<InternalExpressionValue.t>,
|
> => {
|
||||||
// ) => {
|
switch anIType {
|
||||||
// let functionType = switch evFunctionType {
|
| T.ItTypeFunction({inputs}) => isITypeOf(T.ItTypeTuple({elements: inputs}), args->IEvArray)
|
||||||
// | IEvRecord(functionType) => functionType
|
| _ => T.TypeMismatch(anIType, args->IEvArray)->Error
|
||||||
// | _ => 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) => {
|
let checkArguments = (
|
||||||
// statement = `type compiled=${typeExpression}`
|
typeExpressionSourceCode: string,
|
||||||
|
args: array<InternalExpressionValue.t>,
|
||||||
// }
|
reducerFn: ExpressionT.reducerFn,
|
||||||
|
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||||
//TODO: asGuard
|
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||||
|
| Ok(anIType) =>
|
||||||
|
switch checkITypeArguments(anIType, args) {
|
||||||
|
| Ok(_) => Ok(args->IEvArray)
|
||||||
|
| Error(T.TypeMismatch(anIType, evValue)) =>
|
||||||
|
Error(
|
||||||
|
ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
| Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,10 @@
|
||||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||||
|
|
||||||
type internalExpressionValue = InternalExpressionValue.t
|
type internalExpressionValue = InternalExpressionValue.t
|
||||||
|
|
||||||
// module Sample = {
|
|
||||||
// // In real life real libraries should be somewhere else
|
|
||||||
// /*
|
|
||||||
// For an example of mapping polymorphic custom functions. To be deleted after real integration
|
|
||||||
// */
|
|
||||||
// let customAdd = (a: float, b: float): float => {a +. b}
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
*/
|
*/
|
||||||
|
let dispatch = (call: InternalExpressionValue.functionCall, environment, reducer, chain): result<
|
||||||
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
|
|
||||||
let registry = FunctionRegistry_Library.registry
|
|
||||||
|
|
||||||
let tryRegistry = ((fnName, args): InternalExpressionValue.functionCall, env) => {
|
|
||||||
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
|
|
||||||
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let dispatch = (call: InternalExpressionValue.functionCall, environment, chain): result<
|
|
||||||
internalExpressionValue,
|
internalExpressionValue,
|
||||||
'e,
|
'e,
|
||||||
> => {
|
> => {
|
||||||
|
@ -32,9 +13,10 @@ let dispatch = (call: InternalExpressionValue.functionCall, environment, chain):
|
||||||
() => ReducerInterface_Date.dispatch(call, environment),
|
() => ReducerInterface_Date.dispatch(call, environment),
|
||||||
() => ReducerInterface_Duration.dispatch(call, environment),
|
() => ReducerInterface_Duration.dispatch(call, environment),
|
||||||
() => ReducerInterface_Number.dispatch(call, environment),
|
() => ReducerInterface_Number.dispatch(call, environment),
|
||||||
() => tryRegistry(call, environment),
|
() => FunctionRegistry_Library.dispatch(call, environment),
|
||||||
])->E.O2.default(chain(call, environment))
|
])->E.O2.default(chain(call, environment, reducer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ let toStringWithType = aValue =>
|
||||||
| IEvDeclaration(_) => `Declaration::${toString(aValue)}`
|
| IEvDeclaration(_) => `Declaration::${toString(aValue)}`
|
||||||
| IEvDistribution(_) => `Distribution::${toString(aValue)}`
|
| IEvDistribution(_) => `Distribution::${toString(aValue)}`
|
||||||
| IEvLambda(_) => `Lambda::${toString(aValue)}`
|
| IEvLambda(_) => `Lambda::${toString(aValue)}`
|
||||||
| IEvBindings(_) => `Module::${toString(aValue)}`
|
| IEvBindings(_) => `Bindings::${toString(aValue)}`
|
||||||
| IEvNumber(_) => `Number::${toString(aValue)}`
|
| IEvNumber(_) => `Number::${toString(aValue)}`
|
||||||
| IEvRecord(_) => `Record::${toString(aValue)}`
|
| IEvRecord(_) => `Record::${toString(aValue)}`
|
||||||
| IEvString(_) => `String::${toString(aValue)}`
|
| IEvString(_) => `String::${toString(aValue)}`
|
||||||
|
@ -160,6 +160,26 @@ let valueToValueType = value =>
|
||||||
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
| IEvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let externalValueToValueType = (value: ExternalExpressionValue.t) =>
|
||||||
|
switch value {
|
||||||
|
| EvArray(_) => EvtArray
|
||||||
|
| EvArrayString(_) => EvtArrayString
|
||||||
|
| EvBool(_) => EvtBool
|
||||||
|
| EvCall(_) => EvtCall
|
||||||
|
| EvDate(_) => EvtDate
|
||||||
|
| EvDeclaration(_) => EvtDeclaration
|
||||||
|
| EvDistribution(_) => EvtDistribution
|
||||||
|
| EvLambda(_) => EvtLambda
|
||||||
|
| EvModule(_) => EvtModule
|
||||||
|
| EvNumber(_) => EvtNumber
|
||||||
|
| EvRecord(_) => EvtRecord
|
||||||
|
| EvString(_) => EvtString
|
||||||
|
| EvSymbol(_) => EvtSymbol
|
||||||
|
| EvTimeDuration(_) => EvtTimeDuration
|
||||||
|
| EvType(_) => EvtType
|
||||||
|
| EvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
||||||
let (fn, args) = functionCall
|
let (fn, args) = functionCall
|
||||||
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
CallSignature(fn, args->Js.Array2.map(valueToValueType))
|
||||||
|
|
|
@ -631,6 +631,19 @@ module A = {
|
||||||
)
|
)
|
||||||
let filter = Js.Array.filter
|
let filter = Js.Array.filter
|
||||||
let joinWith = Js.Array.joinWith
|
let joinWith = Js.Array.joinWith
|
||||||
|
let transpose = (xs: array<array<'a>>): array<array<'a>> => {
|
||||||
|
let arr: array<array<'a>> = []
|
||||||
|
for i in 0 to length(xs) - 1 {
|
||||||
|
for j in 0 to length(xs[i]) - 1 {
|
||||||
|
if Js.Array.length(arr) <= j {
|
||||||
|
ignore(Js.Array.push([xs[i][j]], arr))
|
||||||
|
} else {
|
||||||
|
ignore(Js.Array.push(xs[i][j], arr[j]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
|
||||||
let all = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) == length(xs)
|
let all = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) == length(xs)
|
||||||
let any = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) > 0
|
let any = (p: 'a => bool, xs: array<'a>): bool => length(filter(p, xs)) > 0
|
||||||
|
@ -752,7 +765,7 @@ module A = {
|
||||||
let diff = (t: t): array<float> =>
|
let diff = (t: t): array<float> =>
|
||||||
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
|
||||||
|
|
||||||
let cumsum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
let cumSum = (t: t): array<float> => accumulate((a, b) => a +. b, t)
|
||||||
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
let cumProd = (t: t): array<float> => accumulate((a, b) => a *. b, t)
|
||||||
|
|
||||||
exception RangeError(string)
|
exception RangeError(string)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user