首页 > 解决方案 > 在 f# 中使用 Result 提取结果或聚合错误的简洁方法

问题描述

我有一个从网格收集数据的系统。它按行组织。处理的每一行都返回一个Result<data, string list>对象。错误案例中的列表是解析过程中遇到的错误列表。每行可以有多个错误,但只有一个有效结果。

data list如果没有错误,我想将数据汇总到一个列表中,或者string list如果至少有一个错误,我想在表单中列出完整的错误列表。

最终的返回类型是Result<data list, string list>

我有以下代码:

let hasErrors =
    gridRows |> List.exists (fun r -> match r with | Ok _ -> false | Error _ -> true)

// build the layer list, or return the errors
match hasErrors with
| false -> Ok (
            gridRows
            |> List.map (fun r ->
                match r with
                | Ok layer -> layer
                | Error _  -> failwith "this should never execute"
            )
          )
| true -> Error (
                gridRows
                |> List.collect (fun r ->
                    match r with
                    | Ok _ -> []
                    | Error messages -> messages
                )
             )

但这感觉非常笨重且难以阅读。

使用 Result<>,有没有办法:

标签: f#

解决方案


您在这里要做的是折叠,这是一种迭代列表的所有元素同时保持某种状态的方法。在这种情况下,状态将是最终结果值 -Ok具有所有值或Error具有所有错误。

折叠功能非常简单:

let f state item = 
    match state, item with
    | Ok prevResults, Ok res -> Ok (prevResults @ [res])
    | Ok _, Error errs -> Error errs
    | Error errs, Ok _ -> Error errs
    | Error prevErrs, Error errs -> Error (prevErrs @ errs)

此函数查看“迄今为止累积的结果”(也称为“状态”)和当前项目,并针对四种可能性中的每一种,返回适当的新“迄今为止累积的结果”。

然后您可以使用此函数折叠列表,初始状态为Ok []

gridRows |> List.fold f (Ok [])

推荐阅读