Merge pull request #2 from foretold-app/Shape-manipulations
Shape manipulations
This commit is contained in:
commit
a40bd21eb9
26
__tests__/Foo/Foo__Test.re
Normal file
26
__tests__/Foo/Foo__Test.re
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
open Jest;
|
||||||
|
open Expect;
|
||||||
|
|
||||||
|
let shape: DistributionTypes.xyShape = {
|
||||||
|
xs: [|1., 4., 8.|],
|
||||||
|
ys: [|8., 9., 2.|],
|
||||||
|
};
|
||||||
|
|
||||||
|
open Shape;
|
||||||
|
|
||||||
|
describe("Shape", () =>
|
||||||
|
describe("XYShape", () =>
|
||||||
|
test("#ySum", ()
|
||||||
|
=>
|
||||||
|
expect(XYShape.ySum(shape)) |> toEqual(19.0)
|
||||||
|
)
|
||||||
|
// test("#both", () => {
|
||||||
|
// let expected: DistributionTypes.xyShape = {
|
||||||
|
// xs: [|1., 4., 8.|],
|
||||||
|
// ys: [|8., 1., 1.|],
|
||||||
|
// };
|
||||||
|
// expect(shape |> XYShape.derivative |> XYShape.integral)
|
||||||
|
// |> toEqual(shape);
|
||||||
|
// });
|
||||||
|
)
|
||||||
|
);
|
|
@ -11,6 +11,11 @@
|
||||||
"dir": "showcase",
|
"dir": "showcase",
|
||||||
"type": "dev",
|
"type": "dev",
|
||||||
"subdirs": true
|
"subdirs": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dir": "__tests__",
|
||||||
|
"type": "dev",
|
||||||
|
"subdirs": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
|
"bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
|
||||||
|
@ -21,6 +26,7 @@
|
||||||
"suffix": ".bs.js",
|
"suffix": ".bs.js",
|
||||||
"namespace": true,
|
"namespace": true,
|
||||||
"bs-dependencies": [
|
"bs-dependencies": [
|
||||||
|
"@glennsl/bs-jest",
|
||||||
"@foretold/components",
|
"@foretold/components",
|
||||||
"bs-ant-design-alt",
|
"bs-ant-design-alt",
|
||||||
"reason-react",
|
"reason-react",
|
||||||
|
|
10
package.json
10
package.json
|
@ -13,7 +13,10 @@
|
||||||
"server": "moduleserve ./ --port 8000",
|
"server": "moduleserve ./ --port 8000",
|
||||||
"predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
|
"predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
|
||||||
"deploy": "gh-pages -d dist",
|
"deploy": "gh-pages -d dist",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "jest",
|
||||||
|
"test:ci": "yarn jest",
|
||||||
|
"watch:test": "jest --watchAll",
|
||||||
|
"watch:s": "yarn jest -- Converter_test --watch"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"BuckleScript",
|
"BuckleScript",
|
||||||
|
@ -26,13 +29,16 @@
|
||||||
"@foretold/cdf": "^1.0.15",
|
"@foretold/cdf": "^1.0.15",
|
||||||
"@foretold/components": "^0.0.3",
|
"@foretold/components": "^0.0.3",
|
||||||
"@foretold/guesstimator": "^1.0.10",
|
"@foretold/guesstimator": "^1.0.10",
|
||||||
|
"@glennsl/bs-jest": "^0.4.9",
|
||||||
"antd": "3.17.0",
|
"antd": "3.17.0",
|
||||||
"autoprefixer": "^9.7.4",
|
"autoprefixer": "^9.7.4",
|
||||||
|
"babel-jest": "^25.1.0",
|
||||||
"bs-ant-design-alt": "2.0.0-alpha.31",
|
"bs-ant-design-alt": "2.0.0-alpha.31",
|
||||||
"bs-css": "^11.0.0",
|
"bs-css": "^11.0.0",
|
||||||
"bs-moment": "0.4.4",
|
"bs-moment": "0.4.4",
|
||||||
"bs-reform": "9.7.1",
|
"bs-reform": "9.7.1",
|
||||||
"d3": "^5.15.0",
|
"d3": "^5.15.0",
|
||||||
|
"jest": "^25.1.0",
|
||||||
"lenses-ppx": "4.0.0",
|
"lenses-ppx": "4.0.0",
|
||||||
"less": "^3.10.3",
|
"less": "^3.10.3",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
@ -57,4 +63,4 @@
|
||||||
"react": "./node_modules/react",
|
"react": "./node_modules/react",
|
||||||
"react-dom": "./node_modules/react-dom"
|
"react-dom": "./node_modules/react-dom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ let data: DistributionTypes.xyShape = {
|
||||||
let mixedDist =
|
let mixedDist =
|
||||||
GenericDistribution.make(
|
GenericDistribution.make(
|
||||||
~generationSource=
|
~generationSource=
|
||||||
GuesstimatorString("mm(uniform(10,12), normal(5,1), [.5,.5])"),
|
GuesstimatorString(
|
||||||
|
"mm(floor(uniform(20, 30)), normal(50,10), [.5,.5])",
|
||||||
|
),
|
||||||
~probabilityType=Pdf,
|
~probabilityType=Pdf,
|
||||||
~domain=Complete,
|
~domain=Complete,
|
||||||
~unit=Unspecified,
|
~unit=Unspecified,
|
||||||
|
@ -42,14 +44,14 @@ let distributions = () =>
|
||||||
<h2> {"Basic Mixed Distribution" |> ReasonReact.string} </h2>
|
<h2> {"Basic Mixed Distribution" |> ReasonReact.string} </h2>
|
||||||
<GenericDistributionChart dist=mixedDist />
|
<GenericDistributionChart dist=mixedDist />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<h2> {"Time Distribution" |> ReasonReact.string} </h2>
|
|
||||||
<GenericDistributionChart dist=timeDist />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2> {"Domain Limited Distribution" |> ReasonReact.string} </h2>
|
|
||||||
<GenericDistributionChart dist=domainLimitedDist />
|
|
||||||
</div>
|
|
||||||
</div>;
|
</div>;
|
||||||
|
// <div>
|
||||||
|
// <h2> {"Time Distribution" |> ReasonReact.string} </h2>
|
||||||
|
// <GenericDistributionChart dist=timeDist />
|
||||||
|
// </div>
|
||||||
|
// <div>
|
||||||
|
// <h2> {"Domain Limited Distribution" |> ReasonReact.string} </h2>
|
||||||
|
// <GenericDistributionChart dist=domainLimitedDist />
|
||||||
|
// </div>
|
||||||
|
|
||||||
let entry = EntryTypes.(entry(~title="Pdf", ~render=distributions));
|
let entry = EntryTypes.(entry(~title="Pdf", ~render=distributions));
|
|
@ -35,7 +35,11 @@ module Continuous = {
|
||||||
|> ReasonReact.string}
|
|> ReasonReact.string}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-2 border ">
|
<th className="px-4 py-2 border ">
|
||||||
{Shape.Continuous.findY(x, Shape.XYShape.integral(data))
|
{Shape.Continuous.findY(
|
||||||
|
x,
|
||||||
|
Shape.XYShape.Range.integrateWithTriangles(data)
|
||||||
|
|> E.O.toExt(""),
|
||||||
|
)
|
||||||
|> E.Float.with2DigitsPrecision
|
|> E.Float.with2DigitsPrecision
|
||||||
|> ReasonReact.string}
|
|> ReasonReact.string}
|
||||||
</th>
|
</th>
|
||||||
|
@ -47,6 +51,51 @@ module Continuous = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module Mixed = {
|
||||||
|
[@react.component]
|
||||||
|
let make = (~data: DistributionTypes.mixedShape) => {
|
||||||
|
let (x, setX) = React.useState(() => 0.);
|
||||||
|
let chart =
|
||||||
|
React.useMemo1(
|
||||||
|
() =>
|
||||||
|
<CdfChart__Plain
|
||||||
|
data={data.continuous}
|
||||||
|
color={`hex("333")}
|
||||||
|
onHover={r => setX(_ => r)}
|
||||||
|
/>,
|
||||||
|
[|data|],
|
||||||
|
);
|
||||||
|
<div>
|
||||||
|
chart
|
||||||
|
<table className="table-auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2"> {"X Point" |> ReasonReact.string} </th>
|
||||||
|
<th className="px-4 py-2"> {"Y Pount" |> ReasonReact.string} </th>
|
||||||
|
<th className="px-4 py-2">
|
||||||
|
{"Y Integral to Point" |> ReasonReact.string}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border ">
|
||||||
|
{x |> E.Float.toString |> ReasonReact.string}
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 border ">
|
||||||
|
{Shape.Mixed.getYIntegral(x, data)
|
||||||
|
|> E.O.fmap(E.Float.with2DigitsPrecision)
|
||||||
|
|> E.O.default("")
|
||||||
|
|> ReasonReact.string}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div />
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
[@react.component]
|
[@react.component]
|
||||||
let make = (~dist) => {
|
let make = (~dist) => {
|
||||||
switch ((dist: option(DistributionTypes.genericDistribution))) {
|
switch ((dist: option(DistributionTypes.genericDistribution))) {
|
||||||
|
@ -62,7 +111,38 @@ let make = (~dist) => {
|
||||||
}) =>
|
}) =>
|
||||||
<div>
|
<div>
|
||||||
<Continuous data=n />
|
<Continuous data=n />
|
||||||
<Continuous data={n |> Shape.XYShape.integral} />
|
<Continuous
|
||||||
|
data={
|
||||||
|
n
|
||||||
|
|> Shape.XYShape.Range.integrateWithTriangles
|
||||||
|
|> E.O.toExt("")
|
||||||
|
|> Shape.XYShape.scaleCdfTo
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Mixed
|
||||||
|
data={
|
||||||
|
continuous:
|
||||||
|
n
|
||||||
|
|> Shape.Continuous.toCdf
|
||||||
|
|> E.O.toExt("")
|
||||||
|
|> Shape.XYShape.scaleCdfTo
|
||||||
|
|> Shape.Continuous.toPdf
|
||||||
|
|> E.O.toExt(""),
|
||||||
|
discrete: d,
|
||||||
|
discreteProbabilityMassFraction: f,
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Continuous
|
||||||
|
data={
|
||||||
|
n
|
||||||
|
|> Shape.XYShape.Range.integrateWithTriangles
|
||||||
|
|> E.O.toExt("")
|
||||||
|
|> Shape.XYShape.Range.derivative
|
||||||
|
|> E.O.toExt("")
|
||||||
|
|> Shape.XYShape.Range.integrateWithTriangles
|
||||||
|
|> E.O.toExt("")
|
||||||
|
}
|
||||||
|
/>
|
||||||
{d |> Shape.Discrete.scaleYToTotal(f) |> Shape.Discrete.render}
|
{d |> Shape.Discrete.scaleYToTotal(f) |> Shape.Discrete.render}
|
||||||
</div>
|
</div>
|
||||||
| _ => <div />
|
| _ => <div />
|
||||||
|
|
|
@ -19,50 +19,103 @@ module XYShape = {
|
||||||
|
|
||||||
let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)};
|
let fmap = (t: t, y): t => {xs: t.xs, ys: t.ys |> E.A.fmap(y)};
|
||||||
|
|
||||||
|
let scaleCdfTo = (~scaleTo=1., t: t) =>
|
||||||
|
switch (_lastElement(t.ys)) {
|
||||||
|
| Some(n) =>
|
||||||
|
let scaleBy = scaleTo /. n;
|
||||||
|
fmap(t, r => r *. scaleBy);
|
||||||
|
| None => t
|
||||||
|
};
|
||||||
|
|
||||||
let yFold = (fn, t: t) => {
|
let yFold = (fn, t: t) => {
|
||||||
E.A.fold_left(fn, 0., t.ys);
|
E.A.fold_left(fn, 0., t.ys);
|
||||||
};
|
};
|
||||||
|
|
||||||
let ySum = yFold((a, b) => a +. b);
|
let ySum = yFold((a, b) => a +. b);
|
||||||
|
|
||||||
|
let fromArray = ((xs, ys)): t => {xs, ys};
|
||||||
let fromArrays = (xs, ys): t => {xs, ys};
|
let fromArrays = (xs, ys): t => {xs, ys};
|
||||||
|
|
||||||
let transverse = (fn, p: t) => {
|
let _transverse = fn =>
|
||||||
let (xs, ys) =
|
Belt.Array.reduce(_, [||], (items, (x, y)) =>
|
||||||
Belt.Array.zip(p.xs, p.ys)
|
switch (_lastElement(items)) {
|
||||||
->Belt.Array.reduce([||], (items, (x, y)) =>
|
| Some((xLast, yLast)) =>
|
||||||
switch (_lastElement(items)) {
|
Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
||||||
| Some((_, yLast)) =>
|
| None => [|(x, y)|]
|
||||||
Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
}
|
||||||
| None => [|(x, y)|]
|
);
|
||||||
}
|
|
||||||
)
|
let _transverseShape = (fn, p: t) => {
|
||||||
|> Belt.Array.unzip;
|
Belt.Array.zip(p.xs, p.ys)
|
||||||
fromArrays(xs, ys);
|
|> _transverse(fn)
|
||||||
|
|> Belt.Array.unzip
|
||||||
|
|> fromArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
let getY = (t: t, x: float) => x;
|
let accumulateYs = _transverseShape((aCurrent, aLast) => aCurrent +. aLast);
|
||||||
|
let subtractYs = _transverseShape((aCurrent, aLast) => aCurrent -. aLast);
|
||||||
|
|
||||||
let integral = transverse((aCurrent, aLast) => aCurrent +. aLast);
|
module Range = {
|
||||||
let derivative = transverse((aCurrent, aLast) => aCurrent -. aLast);
|
// ((lastX, lastY), (nextX, nextY))
|
||||||
|
type zippedRange = ((float, float), (float, float));
|
||||||
|
|
||||||
let massWithin = (t: t, left: pointInRange, right: pointInRange) => {
|
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||||
switch (left, right) {
|
let toT = r => r |> Belt.Array.unzip |> fromArray;
|
||||||
| (Unbounded, Unbounded) => t |> ySum
|
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
||||||
| (Unbounded, X(f)) => t |> integral |> getY(_, f)
|
|
||||||
| (X(f), Unbounded) => ySum(t) -. getY(integral(t), f)
|
let rangeAreaAssumingSteps =
|
||||||
| (X(l), X(r)) => getY(integral(t), r) -. getY(integral(t), l)
|
(((lastX, lastY), (nextX, _)): zippedRange) =>
|
||||||
|
(nextX -. lastX) *. lastY;
|
||||||
|
|
||||||
|
let rangeAreaAssumingTriangles =
|
||||||
|
(((lastX, lastY), (nextX, nextY)): zippedRange) =>
|
||||||
|
(nextX -. lastX) *. (lastY +. nextY) /. 2.;
|
||||||
|
|
||||||
|
let delta_y_over_delta_x =
|
||||||
|
(((lastX, lastY), (nextX, nextY)): zippedRange) =>
|
||||||
|
(nextY -. lastY) /. (nextX -. lastX);
|
||||||
|
|
||||||
|
let inRanges = (mapper, reducer, t: t) => {
|
||||||
|
Belt.Array.zip(t.xs, t.ys)
|
||||||
|
|> E.A.toRanges
|
||||||
|
|> E.R.toOption
|
||||||
|
|> E.O.fmap(r => r |> Belt.Array.map(_, mapper) |> reducer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mapYsBasedOnRanges = fn => inRanges(r => (nextX(r), fn(r)), toT);
|
||||||
|
|
||||||
|
let toStepFn = z => mapYsBasedOnRanges(rangeAreaAssumingSteps, z);
|
||||||
|
|
||||||
|
let integrateWithSteps = z =>
|
||||||
|
mapYsBasedOnRanges(rangeAreaAssumingSteps, z) |> E.O.fmap(accumulateYs);
|
||||||
|
|
||||||
|
let integrateWithTriangles = z =>
|
||||||
|
mapYsBasedOnRanges(rangeAreaAssumingTriangles, z)
|
||||||
|
|> E.O.fmap(accumulateYs);
|
||||||
|
|
||||||
|
let derivative = mapYsBasedOnRanges(delta_y_over_delta_x);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let findY = CdfLibrary.Distribution.findY;
|
||||||
|
let findX = CdfLibrary.Distribution.findX;
|
||||||
};
|
};
|
||||||
|
// let massWithin = (t: t, left: pointInRange, right: pointInRange) => {
|
||||||
|
// switch (left, right) {
|
||||||
|
// | (Unbounded, Unbounded) => t |> ySum
|
||||||
|
// | (Unbounded, X(f)) => t |> integral |> getY(t, 3.0)
|
||||||
|
// | (X(f), Unbounded) => ySum(t) -. getY(integral(t), f)
|
||||||
|
// | (X(l), X(r)) => getY(integral(t), r) -. getY(integral(t), l)
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
module Continuous = {
|
module Continuous = {
|
||||||
let fromArrays = XYShape.fromArrays;
|
let fromArrays = XYShape.fromArrays;
|
||||||
let toJs = XYShape.toJs;
|
let toJs = XYShape.toJs;
|
||||||
let toPdf = CdfLibrary.Distribution.toPdf;
|
let toPdf = XYShape.Range.derivative;
|
||||||
let toCdf = CdfLibrary.Distribution.toCdf;
|
let toCdf = XYShape.Range.integrateWithTriangles;
|
||||||
let findX = CdfLibrary.Distribution.findX;
|
let findX = CdfLibrary.Distribution.findX;
|
||||||
let findY = CdfLibrary.Distribution.findY;
|
let findY = CdfLibrary.Distribution.findY;
|
||||||
|
let findIntegralY = (f, r) => r |> toCdf |> E.O.fmap(findY(f));
|
||||||
};
|
};
|
||||||
|
|
||||||
module Discrete = {
|
module Discrete = {
|
||||||
|
@ -94,6 +147,18 @@ module Discrete = {
|
||||||
| Some((_, y)) => y
|
| Some((_, y)) => y
|
||||||
| None => 0.
|
| None => 0.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let integrate = XYShape.accumulateYs;
|
||||||
|
let derivative = XYShape.subtractYs;
|
||||||
|
|
||||||
|
let findIntegralY = (f, t: t) =>
|
||||||
|
t
|
||||||
|
|> XYShape.Range.toStepFn
|
||||||
|
|> E.O.fmap(XYShape.accumulateYs)
|
||||||
|
|> E.O.fmap(CdfLibrary.Distribution.findY(f));
|
||||||
|
|
||||||
|
let findX = (f, t: t) =>
|
||||||
|
t |> XYShape.Range.toStepFn |> E.O.fmap(CdfLibrary.Distribution.findX(f));
|
||||||
};
|
};
|
||||||
|
|
||||||
module Mixed = {
|
module Mixed = {
|
||||||
|
@ -103,15 +168,41 @@ module Mixed = {
|
||||||
discreteProbabilityMassFraction,
|
discreteProbabilityMassFraction,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mixedMultiply =
|
||||||
|
(
|
||||||
|
t: DistributionTypes.mixedShape,
|
||||||
|
continuousComponent,
|
||||||
|
discreteComponent,
|
||||||
|
) => {
|
||||||
|
let diffFn = t.discreteProbabilityMassFraction;
|
||||||
|
continuousComponent *. (1.0 -. diffFn) +. discreteComponent *. diffFn;
|
||||||
|
};
|
||||||
|
|
||||||
type yPdfPoint = {
|
type yPdfPoint = {
|
||||||
continuous: float,
|
continuous: option(float),
|
||||||
discrete: float,
|
discrete: option(float),
|
||||||
|
discreteProbabilityMassFraction: float,
|
||||||
};
|
};
|
||||||
|
|
||||||
let getY = (t: DistributionTypes.mixedShape, x: float): yPdfPoint => {
|
let getY = (t: DistributionTypes.mixedShape, x: float): yPdfPoint => {
|
||||||
continuous: Continuous.findY(x, t.continuous),
|
continuous: Continuous.findY(x, t.continuous) |> E.O.some,
|
||||||
discrete: Discrete.findY(x, t.discrete),
|
discrete: Discrete.findY(x, t.discrete) |> E.O.some,
|
||||||
|
discreteProbabilityMassFraction: t.discreteProbabilityMassFraction,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let getYIntegral =
|
||||||
|
(x: float, t: DistributionTypes.mixedShape): option(float) => {
|
||||||
|
let c = t.continuous |> Continuous.findIntegralY(x);
|
||||||
|
let d = Discrete.findIntegralY(x, t.discrete);
|
||||||
|
switch (c, d) {
|
||||||
|
| (Some(c), Some(d)) => Some(mixedMultiply(t, c, d))
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//Do the math to add these distributions together
|
||||||
|
// let integral =
|
||||||
|
// (x: float, t: DistributionTypes.mixedShape): option(XYShape.t) => {
|
||||||
|
// };
|
||||||
};
|
};
|
||||||
|
|
||||||
module Any = {
|
module Any = {
|
||||||
|
@ -124,13 +215,6 @@ module Any = {
|
||||||
| Continuous(continuousShape) =>
|
| Continuous(continuousShape) =>
|
||||||
`continuous(Continuous.findY(x, continuousShape))
|
`continuous(Continuous.findY(x, continuousShape))
|
||||||
};
|
};
|
||||||
|
|
||||||
let massInRange = (t: t, left: pointInRange, right: pointInRange) =>
|
|
||||||
switch (t) {
|
|
||||||
| Mixed(m) => 3.0
|
|
||||||
| Discrete(discreteShape) => 2.0
|
|
||||||
| Continuous(continuousShape) => 3.0
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module DomainMixed = {
|
module DomainMixed = {
|
||||||
|
|
19
src/lib/E.re
19
src/lib/E.re
|
@ -114,6 +114,11 @@ module R = {
|
||||||
let id = e => e |> result(U.id, U.id);
|
let id = e => e |> result(U.id, U.id);
|
||||||
let fmap = Rationale.Result.fmap;
|
let fmap = Rationale.Result.fmap;
|
||||||
let bind = Rationale.Result.bind;
|
let bind = Rationale.Result.bind;
|
||||||
|
let toOption = (e: Belt.Result.t('a, 'b)) =>
|
||||||
|
switch (e) {
|
||||||
|
| Ok(r) => Some(r)
|
||||||
|
| Error(_) => None
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let safe_fn_of_string = (fn, s: string): option('a) =>
|
let safe_fn_of_string = (fn, s: string): option('a) =>
|
||||||
|
@ -217,6 +222,20 @@ module A = {
|
||||||
let concatMany = Belt.Array.concatMany;
|
let concatMany = Belt.Array.concatMany;
|
||||||
let keepMap = Belt.Array.keepMap;
|
let keepMap = Belt.Array.keepMap;
|
||||||
let stableSortBy = Belt.SortArray.stableSortBy;
|
let stableSortBy = Belt.SortArray.stableSortBy;
|
||||||
|
let toRanges = (a: array('a)) =>
|
||||||
|
switch (a |> Belt.Array.length) {
|
||||||
|
| 0
|
||||||
|
| 1 => Belt.Result.Error("Must be at least 2 elements")
|
||||||
|
| n =>
|
||||||
|
Belt.Array.makeBy(n - 1, r => r)
|
||||||
|
|> Belt.Array.map(_, index =>
|
||||||
|
(
|
||||||
|
Belt.Array.getUnsafe(a, index),
|
||||||
|
Belt.Array.getUnsafe(a, index + 1),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Rationale.Result.return
|
||||||
|
};
|
||||||
|
|
||||||
let asList = (f: list('a) => list('a), r: array('a)) =>
|
let asList = (f: list('a) => list('a), r: array('a)) =>
|
||||||
r |> to_list |> f |> of_list;
|
r |> to_list |> f |> of_list;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user