rasx
rasx

Reputation: 5338

avoiding nesting `Result<'T,'TError>`

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

Answers (1)

Tomas Petricek
Tomas Petricek

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 fail
  • Result<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 fail
  • Result<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

Related Questions