假设我有一些函数返回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
函数,但是我觉得我不必这样做。
有什么想法可以最好地组合这样的东西?
答案 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 { ... }