使用bind链接连续函数

时间:2017-04-16 12:49:17

标签: .net f# functional-programming monads pipeline

Result type是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创建目录。然后,我尝试使用 {/ strong> 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

1 个答案:

答案 0 :(得分:2)

我不完全确定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))