使用绑定链接连续函数



Result 类型是 F# 4.1 中的新功能:

type Result<'T,'TError> = 
| Ok of 'T
| Error of 'TError
bind : ('T -> Result<'U, 'TError>) -> Result<'T, 'TError> -> Result<'U, 'TError>

如何使用Result.bind函数链接以下示例中的连续函数?

假设我想将一些数据保存到文件中。如果成功,则应返回保存的数据,如果失败,则应返回错误字符串:

首先,我尝试使用 createDirectory createDirectory2 创建一个目录。然后,我尝试使用 createFile getPermissionType 函数创建一个文件。最后,我将数据保存到文件中。

let init data directoryPath =
    (match createDirectory directoryPath with
    | Ok directory -> Ok directory
    | Error err1 ->
        match createDirectory2 directoryPath with
        | Ok directory -> Ok directory
        | Error err2 -> Error (err1 + "; " + err2))
    |> function
       | Ok directory ->
             match (createFile directory directoryPath), (getPermissionType directory) with
             | Ok filePath, Ok permissionType ->
                 Ok (saveData data directory filePath permissionType)
             | Error err1, Ok _ -> Error err1
             | Ok _, Error err2 -> Error err2
             | Error err1, Error err2 -> Error (err1 + "; " + err2)
       | Error err -> err

我不完全确定用于Result实现此处需要的所有 F# 库函数 - bind 操作允许您按顺序编写多个操作,并在第一个错误处停止。

在您的情况下,您希望运行两个可能的函数之一,然后要执行两个操作并收集它们生成的错误。为此,您可能需要定义更多Result函数。像这样的东西可以解决问题(我更改了代码以收集错误列表(:

module Result = 
  let either f1 f2 = 
    match f1 () with
    | Ok res -> Ok res
    | Error e1 -> 
        match f2 () with
        | OK res -> Ok res
        | Error e2 -> (e1 @ e2)
  let both res1 res2 = 
    match res1, res2 with
    | Ok r1, Ok r2 -> Ok (r1, r2)
    | Error e1, Error e2 -> Error (e1 @ e2)
    | Error e, _ | _, Error e -> Error e

现在,您可以按如下方式表达逻辑:

let init data directoryPath =
  Result.either 
    (fun () -> createDirectory directoryPath)
    (fun () -> createDirectory2 directoryPath)
  |> Result.bind (fun directory ->
      Result.both (createFile directory directoryPath) (getPermissionType directory)) 
  |> Result.map (fun (filePath, permissionType) ->
      saveData data directory filePath permissionType))

最新更新