Clean way to extract results, or aggregate errors using Result, in f#

Tho*_*mas 3 f#

I have a system where I collect data from a grid. It is organized in rows. Each row processed returns a Result<data, string list> object. The list in the error case is a list of errors encountered during the parsing. Each row can have multiple errors, but only one valid result.

I would like to aggregate the data to a list data list if there are no errors, or make a complete list of errors, in the string list form if there is at least one error.

The final return type is Result<data list, string list>

I have the following code:

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
                )
             )
Run Code Online (Sandbox Code Playgroud)

But this feels very clunky and hard to read.

Using Result<>, is there a way to do:

  • if any error, collect all error elements in a list and return a result with that list
  • if no errors, collect all the ok elements in a list and return a result with that list

Fyo*_*kin 6

您在这里要做的是折叠,这是一种迭代列表中所有元素的方法,同时保持某种状态。这种情况下的状态将是最终结果值 -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)
Run Code Online (Sandbox Code Playgroud)

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

然后你可以使用这个函数来折叠列表,初始状态是Ok []

gridRows |> List.fold f (Ok [])
Run Code Online (Sandbox Code Playgroud)

  • 这是一个提醒您所有关于匹配的第二行和第三行的“析取模式”的好机会:`| _,错误错误 | 错误 errs, _ -&gt; ...` 更加 [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 。 (2认同)
  • @kaefer 我的目标是清楚地展示四种可能性,同时避免更多必须解释的主题 (2认同)