结合使用“绑定”和异步功能

时间:2019-02-15 16:56:37

标签: f#

假设我有一些函数返回Async<Result<string>>

let getData id = async {
   return Ok (string id)
}

现在,此函数的输入是返回Result<int>的另一个函数的结果。

我正在努力在异步CE中将{2和Result.bind一起构成2。

例如:

let main = async {
    let id = Ok 123
    let! x = id |> Result.bind getData

    return x
}

这不起作用,出现错误:

error FS0001: Type mismatch. Expecting a
    'Result<int,'a> -> Async<'b>'    
but given a
    'Result<int,'a> -> Result<'c,'a>'   

或者如果我不使用let!,我会使用let

error FS0001: Type mismatch. Expecting a
    'int -> Result<'a,'b>'    
but given a
    'int -> Async<Result<string,'c>>

我已经看到一些答案,说它们不使用Result<'a>,而只是让Async异常处理完成了艰苦的工作,但是我遇到Option<'a>Option.bind的同样问题

我知道我可以使用Option.isSome/isNone和/或为Result编写自己的isOk/isError函数,但是我觉得我不必这样做。

有什么想法可以最好地组合这样的东西?

1 个答案:

答案 0 :(得分:7)

问题是Result.bind不能与getData一起使用,因为签名不匹配。 Result.bind期望产生一个Result<>但一个getData产生一个Async<Result<_,_>>的函数。您需要bind的{​​{1}}。

Async<Result<_,_>>定义一个AsyncResult.bind函数,如下所示:

Async<Result<_,_>>

现在您可以将module AsyncResult = let bind fRA vRA = async { let! vR = vRA match vR with | Ok v -> return! fRA v | Error m -> return Error m } 函数与返回getData的函数组成,如下所示:

Result

如果为AsyncResult定义CE,则可以这样编写:

let composed p = resultFunction p |> async.Return |> AsyncResult.bind getData

这是我用于处理let composed2 p = asyncResult { let! id = resultFunction p |> async.Return return! getData id } 的完整实现。

首先为Async<Result<>>提供一些有用的定义:

Result

...以及module Result = open Result let rtn = Ok let toOption r = r |> function Ok v -> Some v | _ -> None let defaultWith f r = r |> function Ok v -> v | Error e -> f e let defaultValue d r = r |> function Ok v -> v | Error _ -> d let failIfTrue m v = if v then m |> Error else Ok () let failIfFalse m v = if not v then m |> Error else Ok () let iter fE f r = r |> map f |> defaultWith fE : unit let get r = r |> defaultWith (string >> failwith) let ofOption f vO = vO |> Option.map Ok |> Option.defaultWith (f >> Error) let insertO vRO = vRO |> Option.map(map Some) |> Option.defaultWith(fun () -> Ok None) let absorbO f vOR = vOR |> bind (ofOption f)

Async

...,现在是module Async = let inline rtn v = async.Return v let inline bind f vA = async.Bind( vA, f) let inline map f = bind (f >> rtn) let inline iterS (f: 'a->unit) = map f >> Async.RunSynchronously let inline iterA f = map f >> Async.Start

AsyncResult

最后,是CE type AsyncResult<'v, 'm> = Async<Result<'v, 'm>> module AsyncResult = let mapError fE v = v |> Async.map (Result.mapError fE) let rtn v = async.Return(Ok v ) let rtnR vR = async.Return vR let iterS fE f vRA = Async.iterS (Result.iter fE f) vRA let iterA fE f vRA = Async.iterA (Result.iter fE f) vRA let bind fRA vRA = async { let! vR = vRA match vR with | Ok v -> return! fRA v | Error m -> return Error m } let inline map f m = bind (f >> rtn) m let rec whileLoop cond fRA = if cond () then fRA () |> bind (fun () -> whileLoop cond fRA) else rtn () let (>>=) v f = bind f v let rec traverseSeq f sq = let folder head tail = f head >>= (fun h -> tail >>= (fun t -> List.Cons(h,t) |> rtn)) Array.foldBack folder (Seq.toArray sq) (rtn List.empty) |> map Seq.ofList let inline sequenceSeq sq = traverseSeq id sq let insertO vRAO = vRAO |> Option.map(map Some) |> Option.defaultWith(fun () -> rtn None) let insertR ( vRAR:Result<_,_>) = vRAR |> function | Error m -> rtn (Error m) | Ok v -> map Ok v let absorbR vRRA = vRRA |> Async.map (Result.bind id) let absorbO f vORA = vORA |> Async.map (Result.absorbO f)

的构建器
asyncResult { ... }