避免嵌套 `Result<'T,'TError>`

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

假设我们有这个 input:

let input =
    [
        "0.10"
        "0.21"
        "forty"
        "5.32"
        "q6.20"
    ]

将此输入映射到 F# 结果导致 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`"]

这是一种普遍接受的收集结果的方式吗?

或者,是否存在可伸缩性和性能方面的问题,以至于整个列表只返回一个聚合结果?像 Result<'T,'TError list> 这样的东西? FsToolkit.ErrorHandling [GitHub] 是否适用于此,或者我是否缺少融入语言的模式?

此解决方案是否会处理列表的列表以避免返回 Result<Result<'T,'TError> list>, 'TError>

您提到的所有选项在某些情况下都有用。

  • list<Result<decimal, string>>表示个别值可能无效,但列表本身的转换不能失败的情况
  • Result<list<Result<decimal, string>>, string> 表示个别值可能无效的情况,还有一些其他原因导致列表处理本身可能失败
  • Result<decimal list, string list> 表示您要么正确处理了整个列表,要么处理失败并且在此过程中收集了多个错误。

我认为有趣的情况是最后一个 - 如果您想获得有效列表或解析错误列表,您可以编写以下内容:

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 [])