2017-05-26 1 views
3

ここに複数の状態のF#MailboxProcessorの例がありますが、なぜそれがコンパイルされているのか不思議ですが、動作は予期せぬものです - F#エージェントは、渡されたラムダ関数のinbox.Receive()私は 複数の非同期{}体の使用が複数の状態を許可する "エキスパートF#3.0"のページ284にある一般的なパターンに従っているようにしようとしていますが、inbox.Receive()それぞれの非同期で使用されますか?複数の再帰非同期ボディを持つF#エージェントは、それぞれ複数のinbox.Receive()を使用できますか?

open System 

let mb1<'T> = MailboxProcessor<string>.Start(fun inbox -> 
         let rec loop1 (n:int) = async { 
            printfn "loop1 entry " 
            let! msg = inbox.Receive() 
            do! Async.Sleep(1000) 
            printfn "loop1 calling loop2" //msg received %A" msg 
            return! loop2 (n+1) } 

         and loop2 (x:int) =  async { 
            printfn "loop2 entry" 
            let! msg2 = inbox.Receive() 
            printfn "loop2 msg received %A" msg2 
            printfn "loop2 calling loop1" 
            return! loop1 (x+1) } 
     loop2 0      
             ) 

mb1.Post("data message 1") 
mb1.Post("data message 2") 

利回り

loop2 entry 
loop2 msg received "data message 1" 
loop2 calling loop1 
loop1 entry 
val it : unit =() 
> 
loop2 entry 
loop2 msg received "data message 2" 
loop2 calling loop1 
loop1 entry 
val it : unit =() 
> 

そうしましょう! msg =ループ1のinbox.Receive()をスキップしますか?私はループ2がリターンで完了したと思ったでしょう!ループ1とそのlet! inbox.Receive()の割り当ては、使用された非同期ブロックに固有です。

答えて

5

これは、いわゆる "value restriction"と関連しています。

mb1に明示的な汎用パラメータを指定したため、値ではなく関数としてコンパイルされます。あまり詳しく説明しなくても、コンパイラーは異なる汎用引数を使用して値にアクセスできるようにするためにこれを実行する必要があります。

mb1を「参照」するたびに、実際には新しいエージェントを作成する関数呼び出しが発生するため、2つの.Post呼び出しが別々のオブジェクトで発生します。

let mb1 = MailboxProcessor<string>(... 

それとも、一度だけ、それを呼び出していることを確認してください:

let mb = mb1 
mb.Post("data message 1") 
mb.Post("data message 2") 
のいずれか、状況を是正ので、それを実際の計算ワンス値を作り、値から汎用的な引数を削除するには

+0

クール、Fyodor、ありがとう!おそらく初心者の間違いなので、私は答えとMSDNのリンクを感謝します。コンパイラの要求ごとに「新しい」を追加しました。「new MailboxProcessor 」 –

+1

私の答えがあなたを助けたら、それを受け入れることを検討しますか? –

+0

あなたの他の答えのいくつかを見て、Fyodor、私は '値の制限'が2回以上出てきたと思う。 –

関連する問題