具有多个递归异步主体的 F# 代理可以在每个主体中使用多个 inbox.Receive() 吗?
Can an F# agent with multiple recursive async bodies use multiple inbox.Receive() in each?
我这里有一个多状态的 F# MailboxProcessor 示例,只是想知道为什么它可以编译但行为是意外的 - F# 代理可以在传入的 lambda 函数中只有一个 inbox.Receive() 语句吗?我正在尝试遵循 "Expert F# 3.0" 第 284 页中提供的一般示例模式,其中
使用多个 async {} 主体允许多个状态,但它并不具体说明 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 = ()
>
所以让!循环 1 中的 msg = inbox.Receive() 被跳过了吗?我还以为loop2是由return完成的呢! loop1 那就是让! inbox.Receive() 的分配特定于它使用的异步块。
这与所谓的“value restriction”有关。
因为您给了 mb1
一个明确的泛型参数,它被编译为一个函数,而不是一个值。无需过多赘述,编译器必须执行此操作以促进使用不同的通用参数访问值的可能性。
因此,每次 "reference" mb1
,实际发生的是一个创建全新代理的函数调用,所以这两个 .Post
调用发生在不同的对象上.
要纠正这种情况,请从值中删除泛型参数,从而使其成为真正的计算一次值:
let mb1 = MailboxProcessor<string>( ...
或者确保只调用一次:
let mb = mb1
mb.Post("data message 1")
mb.Post("data message 2")
我这里有一个多状态的 F# MailboxProcessor 示例,只是想知道为什么它可以编译但行为是意外的 - F# 代理可以在传入的 lambda 函数中只有一个 inbox.Receive() 语句吗?我正在尝试遵循 "Expert F# 3.0" 第 284 页中提供的一般示例模式,其中 使用多个 async {} 主体允许多个状态,但它并不具体说明 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 = ()
>
所以让!循环 1 中的 msg = inbox.Receive() 被跳过了吗?我还以为loop2是由return完成的呢! loop1 那就是让! inbox.Receive() 的分配特定于它使用的异步块。
这与所谓的“value restriction”有关。
因为您给了 mb1
一个明确的泛型参数,它被编译为一个函数,而不是一个值。无需过多赘述,编译器必须执行此操作以促进使用不同的通用参数访问值的可能性。
因此,每次 "reference" mb1
,实际发生的是一个创建全新代理的函数调用,所以这两个 .Post
调用发生在不同的对象上.
要纠正这种情况,请从值中删除泛型参数,从而使其成为真正的计算一次值:
let mb1 = MailboxProcessor<string>( ...
或者确保只调用一次:
let mb = mb1
mb.Post("data message 1")
mb.Post("data message 2")