的函数: let getData id = async { return Ok (string id) } 现在这个函数的输入是另一个函数的结果,该函数返回 Re-6ren">
gpt4 book ai didi

f# - 将 "bind"与异步函数一起使用

转载 作者:行者123 更新时间:2023-12-02 11:57:56 25 4
gpt4 key购买 nike

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

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

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

我正在努力思考如何将 2 与 Result.bind 组合在一起在异步 CE 内。

例如:

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>并让异步异常处理完成艰苦的工作,但我遇到了与 Option<'a> 相同的问题和Option.bind .

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

关于组合这样的东西的最佳方式有什么想法吗?

最佳答案

问题是Result.bind不能与 getData 一起使用因为签名不匹配。 Result.bind期望一个函数产生 Result<>但是getData产生 Async<Result<_,_>> 。您需要一个bind对于 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 的函数的函数像这样:

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

如果您为 AsyncResult 定义了 CE,那么您可以像这样编写它:

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 :

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)

最后,CE 的构建器 asyncResult { ... }

type AsyncResultBuilder() =
member __.ReturnFrom vRA : Async<Result<'v , 'm>> = vRA
member __.ReturnFrom vR : Async<Result<'v , 'm>> = AsyncResult.rtnR vR
member __.Return v : Async<Result<'v , 'm>> = AsyncResult.rtn v
member __.Zero () : Async<Result<unit, 'm>> = AsyncResult.rtn ()
member __.Bind (vRA, fRA) : Async<Result<'b , 'm>> = AsyncResult.bind fRA vRA
member __.Bind (vR , fRA) : Async<Result<'b , 'm>> = AsyncResult.bind fRA (vR |> AsyncResult.rtnR)
member __.Combine (vRA, fRA) : Async<Result<'b , 'm>> = AsyncResult.bind fRA vRA
member __.Combine (vR , fRA) : Async<Result<'b , 'm>> = AsyncResult.bind fRA (vR |> AsyncResult.rtnR)
member __.Delay fRA = fRA
member __.Run fRA = AsyncResult.rtn () |> AsyncResult.bind fRA
member __.TryWith (fRA , hnd) : Async<Result<'a , 'm>> = async { try return! fRA() with e -> return! hnd e }
member __.TryFinally(fRA , fn ) : Async<Result<'a , 'm>> = async { try return! fRA() finally fn () }
member __.Using(resource , fRA) : Async<Result<'a , 'm>> = async.Using(resource, fRA)
member __.While (guard , fRA) : Async<Result<unit, 'a>> = AsyncResult.whileLoop guard fRA
member th.For (s: 'a seq, fRA) : Async<Result<unit, 'b>> = th.Using(s.GetEnumerator (), fun enum ->
th.While(enum.MoveNext,
th.Delay(fun () -> fRA enum.Current)))
let asyncResult = AsyncResultBuilder()


[<AutoOpen>]
module Extensions =
type AsyncResultBuilder with
member __.ReturnFrom (vA: Async<'a> ) : Async<Result<'a, 'b>> = Async.map Ok vA
member __.Bind (vA: Async<'a>, fRA) : Async<Result<'b, 'c>> = AsyncResult.bind fRA (Async.map Ok vA)
member __.Combine (vA: Async<'a>, fRA) : Async<Result<'b, 'c>> = AsyncResult.bind fRA (Async.map Ok vA)

关于f# - 将 "bind"与异步函数一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54713827/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com