Skip to content

Result.fromArrayMap #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/Core__Result.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Curry from "rescript/lib/es6/curry.js";
import * as Caml_option from "rescript/lib/es6/caml_option.js";

function getExn(x) {
if (x.TAG === /* Ok */0) {
Expand Down Expand Up @@ -102,6 +103,43 @@ function cmp(a, b, f) {
}
}

function fromArrayMap(xs, f) {
var oks = [];
var firstError;
var index = 0;
var $$break = false;
while(!$$break) {
var x = xs.at(index);
if (x !== undefined) {
var ok = Curry._1(f, Caml_option.valFromOption(x));
if (ok.TAG === /* Ok */0) {
oks.push(ok._0);
index = index + 1 | 0;
} else {
firstError = ok;
$$break = true;
}
} else {
$$break = true;
}
};
var err = firstError;
if (err !== undefined) {
return err;
} else {
return {
TAG: /* Ok */0,
_0: oks
};
}
}

function fromArray(xs) {
return fromArrayMap(xs, (function (i) {
return i;
}));
}

export {
getExn ,
mapWithDefault ,
Expand All @@ -112,5 +150,7 @@ export {
isError ,
eq ,
cmp ,
fromArray ,
fromArrayMap ,
}
/* No side effect */
30 changes: 30 additions & 0 deletions src/Core__Result.res
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,33 @@ let cmpU = (a, b, f) =>
}

let cmp = (a, b, f) => cmpU(a, b, (. x, y) => f(x, y))

// I don't think tail-call optimization is guaranteed
// so a non-recursive implementation is safer.
// https://chromestatus.com/feature/5516876633341952
let fromArrayMap = (xs, f) => {
let oks = []
let firstError = ref(None)
let index = ref(0)
let break = ref(false)
while !break.contents {
switch xs->Core__Array.at(index.contents) {
| None => break := true
| Some(x) =>
switch f(x) {
| Ok(ok) =>
oks->Core__Array.push(ok)
index := index.contents + 1
| Error(_) as err =>
firstError := Some(err)
break := true
}
}
}
switch firstError.contents {
| None => Ok(oks)
| Some(err) => err
}
}

let fromArray = xs => xs->fromArrayMap(i => i)
26 changes: 26 additions & 0 deletions src/Core__Result.resi
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,29 @@ let eq: (t<'a, 'c>, t<'b, 'd>, ('a, 'b) => bool) => bool
```
*/
let cmp: (t<'a, 'c>, t<'b, 'd>, ('a, 'b) => int) => int

/**
`fromArray(xs)` converts an array of results to a single result. If `xs` only contains `Ok` values, returns an `Ok` that contains an array of those values. Otherwise returns the first `Error`.

## Examples

```rescript
Result.fromArray([Ok(1), Ok(2)]) // Ok([1, 2])
Result.fromArray([Ok(1), Error("a"), Ok(2)]) // Error("a")
Result.fromArray([]) // Ok([])
```
*/
let fromArray: array<result<'a, 'b>> => result<array<'a>, 'b>

/**
`fromArrayMap(xs, f)` lazily applies `f` to each item in `xs`. If there are only `Ok` results, returns an `Ok` that contains an array of those values. Otherwise returns the first `Error`.

## Examples

```rescript
let lessThanTen = n => n < 10 ? Ok(n) : Error(n)
Result.fromArray([6, 12, 18], lessThanTen) // Error(12)
Result.fromArray([3, 6, 9], lessThanTen) // Ok([3, 6, 9])
```
*/
let fromArrayMap: (array<'v>, 'v => result<'a, 'b>) => result<array<'a>, 'b>
226 changes: 226 additions & 0 deletions test/ResultTests.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Test from "./Test.mjs";
import * as Caml_obj from "rescript/lib/es6/caml_obj.js";
import * as Core__Result from "../src/Core__Result.mjs";

var eq = Caml_obj.equal;

var fromArrayTestCases = [
[
"when empty, return empty",
[],
{
TAG: /* Ok */0,
_0: []
}
],
[
"when one error, return it",
[{
TAG: /* Error */1,
_0: "a"
}],
{
TAG: /* Error */1,
_0: "a"
}
],
[
"when one ok, return it",
[{
TAG: /* Ok */0,
_0: 1
}],
{
TAG: /* Ok */0,
_0: [1]
}
],
[
"when all ok, return all",
[
{
TAG: /* Ok */0,
_0: 1
},
{
TAG: /* Ok */0,
_0: 2
},
{
TAG: /* Ok */0,
_0: 3
}
],
{
TAG: /* Ok */0,
_0: [
1,
2,
3
]
}
],
[
"when all error, return first",
[
{
TAG: /* Error */1,
_0: "a"
},
{
TAG: /* Error */1,
_0: "b"
},
{
TAG: /* Error */1,
_0: "c"
}
],
{
TAG: /* Error */1,
_0: "a"
}
],
[
"when mix, return first error",
[
{
TAG: /* Ok */0,
_0: 1
},
{
TAG: /* Error */1,
_0: "a"
},
{
TAG: /* Ok */0,
_0: 2
},
{
TAG: /* Error */1,
_0: "b"
}
],
{
TAG: /* Error */1,
_0: "a"
}
]
];

fromArrayTestCases.forEach(function (param) {
Test.run([
[
"ResultTests.res",
19,
22,
43
],
"fromArray: " + param[0] + ""
], Core__Result.fromArray(param[1]), eq, param[2]);
});

var fromArrayMapTestCases = [
[
"when empty, return empty",
[],
{
TAG: /* Ok */0,
_0: []
}
],
[
"when one error, return it",
[30],
{
TAG: /* Error */1,
_0: "30"
}
],
[
"when one ok, return it",
[2],
{
TAG: /* Ok */0,
_0: [4]
}
],
[
"when all ok, return all",
[
1,
2,
3
],
{
TAG: /* Ok */0,
_0: [
2,
4,
6
]
}
],
[
"when all error, return first",
[
20,
30,
40
],
{
TAG: /* Error */1,
_0: "20"
}
],
[
"when mix, return first error",
[
1,
2,
14,
3,
4
],
{
TAG: /* Error */1,
_0: "14"
}
]
];

function fromArrayMap(n) {
if (n < 10) {
return {
TAG: /* Ok */0,
_0: (n << 1)
};
} else {
return {
TAG: /* Error */1,
_0: n.toString()
};
}
}

fromArrayMapTestCases.forEach(function (param) {
Test.run([
[
"ResultTests.res",
35,
15,
39
],
"fromArrayMap: " + param[0] + ""
], Core__Result.fromArrayMap(param[1], fromArrayMap), eq, param[2]);
});

export {
eq ,
fromArrayTestCases ,
fromArrayMapTestCases ,
fromArrayMap ,
}
/* Not a pure module */
40 changes: 40 additions & 0 deletions test/ResultTests.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
open RescriptCore

let eq = (a, b) => a == b

// ============
// fromArrayMap
// ============

let fromArrayTestCases = [
("when empty, return empty", [], Ok([])),
("when one error, return it", [Error("a")], Error("a")),
("when one ok, return it", [Ok(1)], Ok([1])),
("when all ok, return all", [Ok(1), Ok(2), Ok(3)], Ok([1, 2, 3])),
("when all error, return first", [Error("a"), Error("b"), Error("c")], Error("a")),
("when mix, return first error", [Ok(1), Error("a"), Ok(2), Error("b")], Error("a")),
]

fromArrayTestCases->Array.forEach(((title, input, output)) =>
Test.run(__POS_OF__(`fromArray: ${title}`), input->Result.fromArray, eq, output)
)

let fromArrayMapTestCases = [
("when empty, return empty", [], Ok([])),
("when one error, return it", [30], Error("30")),
("when one ok, return it", [2], Ok([4])),
("when all ok, return all", [1, 2, 3], Ok([2, 4, 6])),
("when all error, return first", [20, 30, 40], Error("20")),
("when mix, return first error", [1, 2, 14, 3, 4], Error("14")),
]

let fromArrayMap = n => n < 10 ? Ok(n * 2) : Error(n->Int.toString)

fromArrayMapTestCases->Array.forEach(((title, input, output)) =>
Test.run(
__POS_OF__(`fromArrayMap: ${title}`),
input->Result.fromArrayMap(fromArrayMap),
eq,
output,
)
)
Loading