Reputation: 5338
Suppose we have this input
:
let input =
[
"0.10"
"0.21"
"forty"
"5.32"
"q6.20"
]
Mapping this input to F# results leads to Result<'T,'TError> list
:
open System
let output =
input
|> List.map (
fun s ->
match Decimal.TryParse s with
| true, i -> Ok i
| _ -> Error $"failed to parse `{s}`"
)
output |> printf "%A"
[Ok 0.10M; Ok 0.21M; Error "failed to parse `forty`"; Ok 5.32M;
Error "failed to parse `q6.20`"]
Is this is a generally accepted way to collect results?
Or, is there a concern for scalability and performance such that only one aggregate Result is returned for the entire list? Something like Result<'T,'TError list>
? Would FsToolkit.ErrorHandling
[GitHub] be available for this or am I missing a pattern baked into the language?
Would this solution handle lists of lists to avoid returning Result<Result<'T,'TError> list>, 'TError>
?
Upvotes: 1
Views: 96
Reputation: 243051
All the options you mention can be useful in some cases.
list<Result<decimal, string>>
represents a case where individual values may be invalid, but the transformation of the list itself cannot failResult<list<Result<decimal, string>>, string>
represents a case where individual values may be invalid and there is also some other reason for which the list processing itself may failResult<decimal list, string list>
represents the case where you either process the whole list correctly, or the processing fails and you collect multiple errors along the way.I think the interesting case is the last one - if you wanted to either get a valid list or a list of parsing errors, you could write the following:
let merged = output |> List.fold (fun res v ->
match v, res with
| Ok v, Ok res -> Ok(v::res)
| Ok _, Error errs -> Error(errs)
| Error e, Error errs -> Error(e::errs)
| Error e, Ok _ -> Error [e]) (Ok [])
Upvotes: 3