Reputation: 12107
with this code:
let a = [3; 4; 5; 6; 7]
let check x = if x % 2 = 0 then Ok x else Error x
let b = a |> List.map check
how can I summarize B as:
I could make a method that does this, but I can't help thinking that this has to be built in, or there has to be a clever simple way to make it happen (I have FsToolkit.ErrorHandling btw, in case it's built in there.
Upvotes: 2
Views: 981
Reputation: 738
The function you need is traverseResultA
in the list functions for FsToolkit.ErrorHandling
. See traverseResultA where it says "This is applicative, collecting all errors. Compare the example ...with traverseResultM." (The latter is a monad which gets the first error).
#r "nuget: FsToolkit.ErrorHandling"
open FsToolkit.ErrorHandling
let a = [3; 4; 5; 6; 7]
let aOk = [4;6]
let aError = [3;5;7]
let check x = if x % 2 = 0 then Ok x else Error x
>
val a : int list = [3; 4; 5; 6; 7]
val aOk : int list = [4; 6]
val aError : int list = [3; 5; 7]
val check : x:int -> Result<int,int>
aOk |> List.traverseResultA check
>
val it : Result<int list,int list> = Ok [4; 6]
aError |> List.traverseResultA check
>
val it : Result<int list,int list> = Error [3; 5; 7]
a |> List.traverseResultA check
>
val it : Result<int list,int list> = Error [3; 5; 7]
An explicit version could be:
let traverseResultA data =
let value = function Ok x -> x | Error y -> y
let filter = function Ok _ -> true | Error _ -> false
if List.forall filter data then data |> List.map value |> Ok
else data |> List.filter (not<<filter) |> List.map value |> Error
>
val traverseResultA : data:Result<'a,'a> list -> Result<'a list,'a list>
See also Scott's article for explaining traverse and applicatives versus monads.
Upvotes: 1
Reputation: 17038
There are a lot of cool ideas in the responses here, but IMHO they are overkill for the problem as described. So just for the record, here's a very simple solution:
let a = [3; 4; 5; 6; 7]
let check x = (x % 2 = 0), x
let b =
a
|> List.map check
|> List.partition fst
|> function
| evens, [] -> Ok evens
| _, odds -> Error odds
Upvotes: 1
Reputation: 5741
You can in fact write the function yourself, once you realize that you want to transform a list of Result<'a,'b>
into a single Result<'a list,'b list>
.
module Result =
let ofList arg =
(arg, Ok[]) ||> List.foldBack (fun t s ->
match t, s with
| Ok x, Ok xs -> Ok(x::xs) // all Ok so far, prepend to state
| Error e, Ok _ -> Error[e] // first Error, discard accumulated state
| Ok _, Error es -> Error es // ignore every Ok after first Error
| Error e, Error es -> Error(e::es) ) // second or later Error, prepend to state
// val ofList : arg:Result<'a,'b> list -> Result<'a list,'b list>
[3; 4; 5; 6; 7]
|> List.map (fun x -> if x % 2 = 0 then Ok x else Error x)
|> Result.ofList
// val it : Result<int list,int list> = Error [3; 5; 7]
Upvotes: 4
Reputation: 424
If I understand your question correctly, you are looking for a traverse
or mapM
function.
They are not built into native fsharp, but implementations exists. I will not link to a particular library here since new libraries might pop up later.
You can read more about about it here: fsharpforfunandprofit
Upvotes: 0