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) => | ||||||
|  |     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.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => { | ||||||
|           Belt.Result.flatMap(acc, _ => |           Belt.Result.flatMap(acc, _ => | ||||||
|         switch Belt.Map.String.get(map, property) { |             switch Belt.Map.String.get(aRecord, property) { | ||||||
|         | Some(propertyValue) => isOfResolvedIType(propertyType, propertyValue) |             | Some(propertyValue) => isITypeOf(propertyType, propertyValue) | ||||||
|         | None => TypeErrorWithProperty(anIType, evValue, property)->Error |             | None => T.TypeMismatch(anIType, evValue)->Error | ||||||
|             } |             } | ||||||
|           ) |           ) | ||||||
|         }) |         }) | ||||||
|  |       } else { | ||||||
|  |         T.TypeMismatch(anIType, evValue)->Error | ||||||
|       } |       } | ||||||
|   let _caseArray = (anIType, evValue, elementType, anArray) => { | 
 | ||||||
|     Belt.Array.reduceWithIndex(anArray, Ok(true), (acc, element, index) => { |     | _ => T.TypeMismatch(anIType, evValue)->Error | ||||||
|       switch isOfResolvedIType(elementType, element) { |     } | ||||||
|  | 
 | ||||||
|  |   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 |             | Ok(_) => acc | ||||||
|       | Error(_) => TypeErrorWithPosition(anIType, evValue, index)->Error |             | 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, _ => | ||||||
|  |         switch acc { | ||||||
|  |         | Ok(false) => | ||||||
|  |           switch isITypeOf(anIType, evValue) { | ||||||
|  |           | Ok(_) => Ok(true) | ||||||
|  |           | Error(_) => acc | ||||||
|  |           } | ||||||
|  |         | _ => acc | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |     ) { | ||||||
|  |     | Ok(true) => Ok(true) | ||||||
|  |     | Ok(false) => T.TypeMismatch(anIType, evValue)->Error | ||||||
|  |     | Error(error) => Error(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