From 390ac2e2bb54ade3dcdb832f9bf9c750cd75639b Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 20 May 2022 22:53:53 -0400 Subject: [PATCH] Renamed itype->frType, value-> frValue --- .../FunctionRegistry_Core.res | 188 +++++++++--------- .../FunctionRegistry_Helpers.res | 37 ++-- .../FunctionRegistry_Library.res | 2 +- .../squiggle-lang/src/rescript/Utility/E.res | 3 + 4 files changed, 121 insertions(+), 109 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index 23e1e780..5e9e877e 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -1,43 +1,39 @@ type expressionValue = ReducerInterface_ExpressionValue.expressionValue -type rec itype = - | I_Number - | I_Numeric - | I_DistOrNumber - | I_Record(iRecord) - | I_Array(array) - | I_Option(itype) -and iRecord = array -and iRecordParam = (string, itype) +/* + Function Registry "Type". A type, without any other information. + Like, #Float +*/ +type rec frType = + | FRTypeNumber + | FRTypeNumeric + | FRTypeDistOrNumber + | FRTypeRecord(frTypeRecord) + | FRTypeArray(array) + | FRTypeOption(frType) +and frTypeRecord = array +and frTypeRecordParam = (string, frType) -module Itype = { - let rec toString = (t: itype) => - switch t { - | I_Number => "number" - | I_Numeric => "numeric" - | I_DistOrNumber => "distOrNumber" - | I_Record(r) => { - let input = ((name, itype): iRecordParam) => `${name}: ${toString(itype)}` - `record({${r->E.A2.fmap(input)->E.A2.joinWith(", ")}})` - } - | I_Array(r) => `record(${r->E.A2.fmap(toString)->E.A2.joinWith(", ")})` - | I_Option(v) => `option(${toString(v)})` - } +/* + Function Registry "Value". A type, with the information of that type. + Like, #Float(40.0) +*/ +type rec frValue = + | FRValueNumber(float) + | FRValueDist(DistributionTypes.genericDist) + | FRValueOption(option) + | FRValueDistOrNumber(frValueDistOrNumber) + | FRValueRecord(frValueRecord) +and frValueRecord = array +and frValueRecordParam = (string, frValue) +and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist) + +type fnDefinition = { + name: string, + inputs: array, + run: array => result, } -type rec value = - | Number(float) - | Dist(DistributionTypes.genericDist) - | Option(option) - | DistOrNumber(distOrNumber) - | Record(record) -and record = array<(string, value)> -and distOrNumber = Number(float) | Dist(DistributionTypes.genericDist) - -type runFn = array => result - -type fnDefinition = {name: string, inputs: array, run: runFn} - type function = { name: string, definitions: array, @@ -45,31 +41,58 @@ type function = { type registry = array -let rec matchInput = (input: itype, r: expressionValue): option => - switch (input, r) { - | (I_Number, EvNumber(f)) => Some(Number(f)) - | (I_DistOrNumber, EvNumber(f)) => Some(DistOrNumber(Number(f))) - | (I_DistOrNumber, EvDistribution(Symbolic(#Float(f)))) => Some(DistOrNumber(Number(f))) - | (I_DistOrNumber, EvDistribution(f)) => Some(DistOrNumber(Dist(f))) - | (I_Numeric, EvNumber(f)) => Some(Number(f)) - | (I_Numeric, EvDistribution(Symbolic(#Float(f)))) => Some(Number(f)) - | (I_Option(v), _) => Some(Option(matchInput(v, r))) - | (I_Record(recordParams), EvRecord(record)) => { - let getAndMatch = (name, input) => - E.Dict.get(record, name)->E.O.bind(v => matchInput(input, v)) - let arrayOfNameValues: array<(Js.Dict.key, option)> = - recordParams->E.A2.fmap(((name, input)) => (name, getAndMatch(name, input))) - let hasNullValues = E.A.hasBy(arrayOfNameValues, ((_, value)) => E.O.isNone(value)) - if hasNullValues { - None - } else { - arrayOfNameValues - ->E.A2.fmap(((name, value)) => (name, value->E.O2.toExn(""))) - ->(r => Some(Record(r))) +module FRType = { + type t = frType + let rec toString = (t: t) => + switch t { + | FRTypeNumber => "number" + | FRTypeNumeric => "numeric" + | FRTypeDistOrNumber => "frValueDistOrNumber" + | FRTypeRecord(r) => { + let input = ((name, frType): frTypeRecordParam) => `${name}: ${toString(frType)}` + `record({${r->E.A2.fmap(input)->E.A2.joinWith(", ")}})` } + | FRTypeArray(r) => `record(${r->E.A2.fmap(toString)->E.A2.joinWith(", ")})` + | FRTypeOption(v) => `option(${toString(v)})` + } + + let rec matchWithExpressionValue = (input: t, r: expressionValue): option => + switch (input, r) { + | (FRTypeNumber, EvNumber(f)) => Some(FRValueNumber(f)) + | (FRTypeDistOrNumber, EvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f))) + | (FRTypeDistOrNumber, EvDistribution(Symbolic(#Float(f)))) => + Some(FRValueDistOrNumber(FRValueNumber(f))) + | (FRTypeDistOrNumber, EvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f))) + | (FRTypeNumeric, EvNumber(f)) => Some(FRValueNumber(f)) + | (FRTypeNumeric, EvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f)) + | (FRTypeOption(v), _) => Some(FRValueOption(matchWithExpressionValue(v, r))) + | (FRTypeRecord(recordParams), EvRecord(record)) => { + let getAndMatch = (name, input) => + E.Dict.get(record, name)->E.O.bind(matchWithExpressionValue(input)) + //All names in the type must be present. If any are missing, the corresponding + //value will be None, and this function would return None. + let namesAndValues: array> = + recordParams->E.A2.fmap(((name, input)) => + getAndMatch(name, input)->E.O2.fmap(match => (name, match)) + ) + namesAndValues->E.A.O.openIfAllSome->E.O2.fmap(r => FRValueRecord(r)) + } + | _ => None + } + + let matchWithExpressionValueArray = (inputs: array, args: array): option< + array, + > => { + let isSameLength = E.A.length(inputs) == E.A.length(args) + if !isSameLength { + None + } else { + E.A.zip(inputs, args) + ->E.A2.fmap(((input, arg)) => matchWithExpressionValue(input, arg)) + ->E.A.O.openIfAllSome } - | _ => None } +} module Matcher = { module MatchSimple = { @@ -107,19 +130,8 @@ module Matcher = { module FnDefinition = { type definitionMatch = MatchSimple.t - let getArgValues = (f: fnDefinition, args: array): option> => { - let mainInputTypes = f.inputs - if E.A.length(f.inputs) !== E.A.length(args) { - None - } else { - E.A.zip(mainInputTypes, args) - ->E.A2.fmap(((input, arg)) => matchInput(input, arg)) - ->E.A.O.openIfAllSome - } - } - let matchAssumingSameName = (f: fnDefinition, args: array) => { - switch getArgValues(f, args) { + switch FRType.matchWithExpressionValueArray(f.inputs, args) { | Some(_) => MatchSimple.FullMatch | None => MatchSimple.SameNameDifferentArguments } @@ -228,43 +240,37 @@ module Matcher = { module FnDefinition = { type t = fnDefinition - let getArgValues = (t: t, args: array): option> => { - let mainInputTypes = t.inputs - if E.A.length(t.inputs) !== E.A.length(args) { - None - } else { - E.A.zip(mainInputTypes, args) - ->E.A2.fmap(((input, arg)) => matchInput(input, arg)) - ->E.A.O.openIfAllSome - } - } - let defToString = (t: t) => t.inputs->E.A2.fmap(Itype.toString)->E.A2.joinWith(", ") + let defToString = (t: t) => t.inputs->E.A2.fmap(FRType.toString)->E.A2.joinWith(", ") let run = (t: t, args: array) => { - let argValues = getArgValues(t, args) + let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) switch argValues { | Some(values) => t.run(values) - | None => Error("Impossible") + | None => Error("Incorrect Types") } } -} -module Function = { - type definitionId = int - let make = (~name, ~definitions): function => { - name: name, - definitions: definitions, - } - - let makeDefinition = (~name, ~inputs, ~run): fnDefinition => { + let make = (~name, ~inputs, ~run): fnDefinition => { name: name, inputs: inputs, run: run, } } +module Function = { + let make = (~name, ~definitions): function => { + name: name, + definitions: definitions, + } +} + module Registry = { + /* + 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 + called. However, for now, we could just call the registry last. + */ let matchAndRun = (r: registry, fnName: string, args: array) => { let matchToDef = m => Matcher.Registry.matchToDef(r, m) let showNameMatchDefinitions = matches => { diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res index aa2fa4b1..8ef27a6c 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res @@ -9,34 +9,37 @@ module Wrappers = { } module Prepare = { - let recordWithTwoArgsToValues = (inputs: array): result, string> => + let recordWithTwoArgsToValues = (inputs: array): result, string> => switch inputs { - | [Record([(_, n1), (_, n2)])] => Ok([n1, n2]) + | [FRValueRecord([(_, n1), (_, n2)])] => Ok([n1, n2]) | _ => Error(impossibleError) } - let twoNumberInputs = (inputs: array): result<(float, float), string> => { + let twoNumberInputs = (inputs: array): result<(float, float), string> => { switch inputs { - | [Number(n1), Number(n2)] => Ok(n1, n2) + | [FRValueNumber(n1), FRValueNumber(n2)] => Ok(n1, n2) | _ => Error(impossibleError) } } - let twoDistOrNumber = (values: array): result<(distOrNumber, distOrNumber), string> => { + let twoDistOrNumber = (values: array): result< + (frValueDistOrNumber, frValueDistOrNumber), + string, + > => { switch values { - | [DistOrNumber(a1), DistOrNumber(a2)] => Ok(a1, a2) + | [FRValueDistOrNumber(a1), FRValueDistOrNumber(a2)] => Ok(a1, a2) | _ => Error(impossibleError) } } - let twoDistOrNumberFromRecord = (values: array) => + let twoDistOrNumberFromRecord = (values: array) => values->recordWithTwoArgsToValues->E.R.bind(twoDistOrNumber) } module Process = { let twoDistsOrNumbersToDist = ( ~fn: ((float, float)) => result, - ~values: (distOrNumber, distOrNumber), + ~values: (frValueDistOrNumber, frValueDistOrNumber), ) => { let toSampleSet = r => GenericDist.toSampleSetDist(r, 1000) let sampleSetToExpressionValue = ( @@ -65,10 +68,10 @@ module Process = { } switch values { - | (Number(a1), Number(a2)) => fn((a1, a2))->E.R2.fmap(Wrappers.evDistribution) - | (Dist(a1), Number(a2)) => singleVarSample(a1, r => fn((r, a2))) - | (Number(a1), Dist(a2)) => singleVarSample(a2, r => fn((a1, r))) - | (Dist(a1), Dist(a2)) => { + | (FRValueNumber(a1), FRValueNumber(a2)) => fn((a1, a2))->E.R2.fmap(Wrappers.evDistribution) + | (FRValueDist(a1), FRValueNumber(a2)) => singleVarSample(a1, r => fn((r, a2))) + | (FRValueNumber(a1), FRValueDist(a2)) => singleVarSample(a2, r => fn((a1, r))) + | (FRValueDist(a1), FRValueDist(a2)) => { let altFn = (a, b) => fn((a, b))->mapFnResult let sampleSetResult = E.R.merge(toSampleSet(a1), toSampleSet(a2)) @@ -95,23 +98,23 @@ module TwoArgDist = { r->E.R.bind(Process.twoDistsOrNumbersToDistUsingSymbolicDist(~fn, ~values=_)) let mkRegular = (name, fn) => { - Function.makeDefinition(~name, ~inputs=[I_DistOrNumber, I_DistOrNumber], ~run=inputs => + FnDefinition.make(~name, ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=inputs => inputs->Prepare.twoDistOrNumber->process(~fn) ) } let mkDef90th = (name, fn) => { - Function.makeDefinition( + FnDefinition.make( ~name, - ~inputs=[I_Record([("p5", I_DistOrNumber), ("p95", I_DistOrNumber)])], + ~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])], ~run=inputs => inputs->Prepare.twoDistOrNumberFromRecord->process(~fn), ) } let mkDefMeanStdev = (name, fn) => { - Function.makeDefinition( + FnDefinition.make( ~name, - ~inputs=[I_Record([("mean", I_DistOrNumber), ("stdev", I_DistOrNumber)])], + ~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])], ~run=inputs => inputs->Prepare.twoDistOrNumberFromRecord->process(~fn), ) } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index fe579f2c..7e77da63 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -52,7 +52,7 @@ let more = [ ), Function.make( ~name="To", - ~definitions=[TwoArgDist.mkRegular("cauchy", twoArgs(SymbolicDist.From90thPercentile.make))], + ~definitions=[TwoArgDist.mkRegular("to", twoArgs(SymbolicDist.From90thPercentile.make))], ) ] diff --git a/packages/squiggle-lang/src/rescript/Utility/E.res b/packages/squiggle-lang/src/rescript/Utility/E.res index afac3bbe..fe00d9bb 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E.res +++ b/packages/squiggle-lang/src/rescript/Utility/E.res @@ -2,6 +2,9 @@ Some functions from modules `L`, `O`, and `R` below were copied directly from running `rescript convert -all` on Rationale https://github.com/jonlaing/rationale */ + +let equals = (a,b) => a === b + module FloatFloatMap = { module Id = Belt.Id.MakeComparable({ type t = float