2017-05-26 22 views
3

我在這裏有一個多狀態的F#MailboxProcessor示例,只是想知道爲什麼它會編譯,但行爲是意想不到的 - F#代理只能在lambda函數中傳入一個inbox.Receive()語句嗎?我試圖遵循「Expert F#3.0」第284頁中提供的一般示例模式,其中 多個async {}主體的使用允許多個狀態,但它並不特定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 = inbox.Receive()在循環1中被跳過?我會認爲loop2是通過返回來完成的! loop1和那個let! inbox.Receive()的賦值是特定於其使用的異步塊的。

回答

5

這與所謂的「value restriction」有關。

因爲你給了mb1一個明確的泛型參數,它將被編譯爲一個函數,而不是一個值。沒有太多細節,編譯器必須這樣做,以便利用不同泛型參數訪問值的可能性。因此,每當您「參考」mb1時,實際發生的是創建全新代理的函數調用,因此兩個.Post調用發生在不同的對象上。

爲了改變這種局面,無論是從價值除去一般的參數,從而使其成爲真正的計算一次值:

let mb1 = MailboxProcessor<string>(... 

,或者確保你調用它只有一次:

let mb = mb1 
mb.Post("data message 1") 
mb.Post("data message 2") 
+0

酷,費奧多,謝謝!可能是初學者的錯誤,所以我很欣賞答案和MSDN鏈接。我還爲每個編譯器的請求添加了一個「新的」:「new MailboxProcessor 」 –

+1

如果我的答案對您有幫助,您會考慮接受它嗎? –

+0

通過您的其他答案,費奧多,我看到「價值限制」已經出現了不止一次。 –

相關問題