带F#和异步的Monadic重试逻辑? [英] Monadic Retry logic w/ F# and async?

查看:82
本文介绍了带F#和异步的Monadic重试逻辑?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了以下代码段:

http://fssnip.net/8o

但是我不仅在处理可重用函数,而且还在使用异步此类,而且我想知道如何正确地创建此类型.我有一个很小的retryAsync monad,我想用它代替异步计算,但是它包含重试逻辑,我想知道如何将它们组合在一起?

But I'm working not only with retriable functions, but also with asynchronous such, and I was wondering how I make this type properly. I have a tiny piece of retryAsync monad that I'd like to use as a replacement for async computations, but that contains retry logic, and I'm wondering how I combine them?

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-> Async<'c>

This expression was expected to have type Async<'a> but here has type 'b -> Async<'c>

推荐答案

您的Bind操作的行为类似于async的常规Bind操作,因此您的代码大部分是在async.但是,您的Return类型不正确(应该为'T -> Async<'T>),您的Delay也不同于async的常规Delay.通常,您应该从BindReturn开始-使用Run有点棘手,因为Run用于包装整个foo { .. }块,因此它没有通常的良好可组合性.

Your Bind operation behaves like a normal Bind operation of async, so your code is mostly a re-implementation (or wrapper) over async. However, your Return does not have the right type (it should be 'T -> Async<'T>) and your Delay is also different than normal Delay of async. In general, you should start with Bind and Return - using Run is a bit tricky, because Run is used to wrap the entire foo { .. } block and so it does not give you the usual nice composability.

F#规范和免费的第12章都显示了实现这些操作时应遵循的常用类型,因此我不再赘述.在这里.

The F# specification and a free chapter 12 from Real-World Functional Programming both show the usual types that you should follow when implementing these operations, so I won't repeat that here.

您的方法的主要问题是,您尝试仅在Run中重试计算,但是您所指的重试生成器尝试重试使用let!调用的每个操作.您的方法可能就足够了,但是如果是这样,您可以实现一个试图运行常规Async<'T>并重试的函数:

The main issue with your approach is that you're trying to retry the computation only in Run, but the retry builder that you're referring to attempts to retry each individual operation called using let!. Your approach may be sufficient, but if that's the case, you can just implement a function that tries to run normal Async<'T> and retries:

 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 }

如果您实际上想要实现一个计算生成器,该计算生成器将隐式尝试重试每个异步操作,那么您可以编写类似以下内容的内容(它只是一个草图,但是应该为您指明正确的方向):

If you actually want to implement a computation builder that will implicitly try to retry every single asynchronous operation, then you can write something like the following (it is just a sketch, but it should point you in the right direction):

// 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) }

这篇关于带F#和异步的Monadic重试逻辑?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆