Can an F # agent with multiple recursive asynchronous bodies use multiple inbox.Receive () in each?

I have a multi-state F # MailboxProcessor example, just wondering why it compiles, but the behavior is unexpected - can F # agents only have one inbox.Receive () statement in a passed in lambda function? I am trying to follow the generic example pattern provided in "Expert F # 3.0" on page 284 where using multiple async bodies {} allows multiple states, but is not specific as to whether inbox.Receive () can be used in each async mode?

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")

      

gives

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 = ()
> 

      

so let it be! msg = inbox.Receive () is skipped on loop 1? I would have thought that loop2 ended up returning! loop1 and what let! the purpose of inbox.Receive () is specific to the async block in which it was used.

+3


source to share


1 answer


This is due to the so-called " value limitation ".

Since you gave mb1

an explicit generic parameter, it compiles as a function, not a value. Without going into details, the compiler should do this in order to make it easier to access the value using different generic arguments.

As a result, every time you reference mb1

, there is actually a function call that creates a completely new agent, so the two calls .Post

are on different objects.

To remedy the situation, either remove the common argument from the value, thereby making it a real computed value:



let mb1 = MailboxProcessor<string>( ...

      

Or make sure you only call it once:

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

      

+5


source







All Articles