我找到了这个片段:
但我不仅使用可重复的功能,而且还使用异步功能,我想知道我是如何正确地使用这种类型的。我有一小段retryAsync
monad,我想用它作为异步计算的替代,但它包含重试逻辑,我想知道我是如何组合它们的?
type AsyncRetryBuilder(retries) =
member x.Return a = a // Enable 'return'
member x.ReturnFrom a = x.Run a
member x.Delay f = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Bind expr f = async {
let! tmp = expr
return tmp
}
member x.Zero = failwith "Zero"
member x.Run (f : unit -> Async<_>) : _ =
let rec loop = function
| 0, Some(ex) -> raise ex
| n, _ ->
try
async { let! v = f()
return v }
with ex -> loop (n-1, Some(ex))
loop(retries, None)
let asyncRetry = AsyncRetryBuilder(4)
使用代码是这样的:
module Queue =
let desc (nm : NamespaceManager) name = asyncRetry {
let! exists = Async.FromBeginEnd(name, nm.BeginQueueExists, nm.EndQueueExists)
let beginCreate = nm.BeginCreateQueue : string * AsyncCallback * obj -> IAsyncResult
return! if exists then Async.FromBeginEnd(name, nm.BeginGetQueue, nm.EndGetQueue)
else Async.FromBeginEnd(name, beginCreate, nm.EndCreateQueue)
}
let recv (client : MessageReceiver) timeout =
let bRecv = client.BeginReceive : TimeSpan * AsyncCallback * obj -> IAsyncResult
asyncRetry {
let! res = Async.FromBeginEnd(timeout, bRecv, client.EndReceive)
return res }
错误是:
此表达式的类型为
Async<'a>
,但此处的类型为'b - &gt;Async<'c>
答案 0 :(得分:6)
您的Bind
操作行为与Bind
的正常async
操作相似,因此您的代码主要是async
上的重新实现(或包装)。但是,您的Return
没有正确的类型(应该是'T -> Async<'T>
),而您的Delay
也不同于Delay
的正常async
。一般情况下,您应该从Bind
和Return
开始 - 使用Run
有点棘手,因为Run
用于包装整个foo { .. }
块,所以它没有给你通常很好的可组合性。
真实世界函数式编程中的F# specification和免费chapter 12都显示了实现这些操作时应遵循的常用类型,因此我在此不再重复。
您的方法的主要问题是您尝试仅在Run
中重试计算,但您所指的重试构建器尝试使用let!
重试每个单独的操作。您的方法可能就足够了,但如果是这种情况,您可以实现一个尝试运行正常Async<'T>
并重试的函数:
let RetryRun count (work:Async<'T>) = async {
try
// Try to run the work
return! work
with e ->
// Retry if the count is larger than 0, otherwise fail
if count > 0 then return! RetryRun (count - 1) work
else return raise e }
如果你真的想要实现一个会隐式尝试重试每一个异步操作的计算构建器,那么你可以编写类似下面的内容(它只是一个草图,但它应该指向正确的方向):< / p>
// We're working with normal Async<'T> and
// attempt to retry it until it succeeds, so
// the computation has type Async<'T>
type RetryAsyncBuilder() =
member x.ReturnFrom(comp) = comp // Just return the computation
member x.Return(v) = async { return v } // Return value inside async
member x.Delay(f) = async { return! f() } // Wrap function inside async
member x.Bind(work, f) =
async {
try
// Try to call the input workflow
let! v = work
// If it succeeds, try to do the rest of the work
return! f v
with e ->
// In case of exception, call Bind to try again
return! x.Bind(work, f) }