2012-02-09 10 views
1

は、私はこのスニペットを見つけた:F#と非同期のMonadicリトライロジック?

http://fssnip.net/8o

しかし、私だけではなく再試行機能を備えた、だけでなく、非同期で働いている、と私は適切にこのタイプを作るどのように私は思っていました。私はretryAsyncモナドの小さな断片を持っていますが、私は非同期計算の代わりに使っていますが、再試行論理が含まれています。 >Async<'c>

- b」が入力したここ

この式は、タイプAsync<'a>を持つことが期待されたが、:

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 } 

エラーは次のとおりです。

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) 

消費コードは次のようです

+0

? – Daniel

+0

特定のエラーに関して、あなたの 'Bind'は、引数をタプル(' x.Bind expr f'の代わりに 'x.Bind(expr、f)'と書く)として引数を取るべきです。それがおそらく原因です。しかし、 'f'も全く使用していません。これは非常に疑わしい(あなたの' Return'は間違ったタイプです)。 –

答えて

6

Bind操作は次のように動作します通常のBindの操作はasyncなので、コードはほとんどがasyncの再実装(またはラッパー)です。ただし、Returnには正しいタイプ('T -> Async<'T>)があり、Delayは通常のDelayasync)とは異なります。一般に、Runを使用すると、Runがブロック全体を包むために使用されているので、通常は素晴らしい合成性を与えるわけではありません。Runを使用すると、BindReturnで開始する必要があります。

F# specificationとReal-World Functional Programmingの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 } 

を単一の非同期操作は、あなたは(それだけでスケッチですが、それは正しい方向にあなたを指している必要があります)次のようなものを書くことができます:どこでエラーが(ライン)を発生している

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

再試行<'a>をスニペットから非同期<'a>でラップすることはできますか? – Henrik