【问题标题】:avoiding nesting `Result<'T,'TError>`避免嵌套`Result<'T,'TError>`
【发布时间】:2022-01-27 16:02:00
【问题描述】:

假设我们有这个input:

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

将此输入映射到 F# 结果会导致 Result&lt;'T,'TError&gt; 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&lt;'T,'TError list&gt; 这样的东西? FsToolkit.ErrorHandling [GitHub] 是否可以用于此功能,还是我错过了语言中的一种模式?

此解决方案是否会处理列表列表以避免返回 Result&lt;Result&lt;'T,'TError&gt; list&gt;, 'TError&gt;

【问题讨论】:

  • 这有点取决于你的目标是什么。您可能想要Results 的列表,或者您可能想要收集所有Ok 元素并丢弃所有Error 元素,或者您可能想要收集所有Ok 元素,只要没有@ 987654334@s,在这种情况下只返回其中的第一个。以此类推。
  • 您甚至可以“聚合/折叠/减少 OK,并列出连接错误”

标签: error-handling f#


【解决方案1】:

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

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

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

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

【讨论】:

  • 这是一个很好的答案,远远超出了经典的“它取决于”响应:) ...现在,与遍历“太多次”相关的问题呢? ...嗯,看起来您的 merged 函数正在解决这个问题
猜你喜欢
  • 1970-01-01
  • 2011-02-16
  • 2016-11-19
  • 2014-08-23
  • 2018-10-07
  • 2023-01-30
  • 1970-01-01
  • 2019-03-24
  • 1970-01-01
相关资源
最近更新 更多