Behind the scenes it’s still a JS array, so it’s not actually immutable. If you see unit in the return type you’ll know it most likely mutates something
You can do the same thing using concat and an inner loop, I typically do something very similar in my codebase
let sequence = results => {
let rec loop = (i, oks) =>
switch results[i] {
| None => Ok(oks)
| Some(Ok(x)) => loop(i + 1, Js.Array.concat(oks, [x]))
| Some(Error(_) as e) => e
}
loop(0, [])
}
By the way, it is not the exact signature that you asked for, but I have found a function like this to be very useful in cases where you need to keep track of all the errors and not only the first one.
let combineErrors:
array<result<'ok, 'error>> => result<array<'ok>, array<'error>>
Quick question, as this has come up in other contexts: how do you end up with a array of result?
Generally, you use result when you care about the error, but pretty much any instance when you convert an array of result to a result of array you’re throwing away any info of what the error was.
So I would question the use of result to begin with: in addition to all that ceremony of carrying around all the error information, you end up throwing it away, which seems puzzling.
Others have given some examples, but another I have found helpful is parsing config files and other user input. Gather up all the errors the user made and dump them all to a log or however you want to handle them.
As for throwing data away, Result.fromArrayMap in my PR is somewhat lazy. It evaluates each input in turn for errors and stops calculating when an error is found.
In my case, the frontend is performing a mass operation, so I iterate through an array of inputs, perform a POST, gather the promises, and then need to separate the successful ones from the failures in order to show the user:
Perhaps the following is a more general function that could be added to the library if it could be helpful? One thing I like about this variant is that it strips the Ok/Error constructors and returns the underlying array<'a> and array<'b>, which is something I find myself doing all the time after splitting the Results.
@ocaml.doc(
"Splits an array of Result values into an array of the underlying Ok values and one of the Error values."
)
let partition = (results: array<Belt.Result.t<'a, 'b>>): (array<'a>, array<'b>) => {
Belt.Array.reduce(results, ([], []), ((oks, errs), x) =>
switch x {
| Belt.Result.Ok(ok) => Js.Array2.push(oks, ok)->ignore; (oks, errs)
| Belt.Result.Error(err) => Js.Array2.push(errs, err)->ignore; (oks, errs)
})
}
I think using the JS push function is more efficient than using Belt.Array.concat because the latter creates a new array and copies the original array every time, so partition would become an O(n^2) operation. But is there a better way to do it?