Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MailboxProcessor.PostAndReply design choice

Looking at:

member this.PostAndReply : (AsyncReplyChannel<'Reply> -> 'Msg) * ?int -> 'Reply

I can't figure out why the signature looks so counter-intuitive to me. What we want to do is posting a message to an agent, and wait for a reply. Why do we have to give him a weird function as a 'message'?

See again this MSDN snippet:

let rec loop() =
    printf "> "
    let input = Console.ReadLine()
    printThreadId("Console loop")
    let reply = agent.PostAndReply(fun replyChannel -> input, replyChannel)
    if (reply <> "Stopping.") then
        printfn "Reply: %s" reply
       loop()
    else
        ()
loop()

I'd rather prefer something like this:

member this.PostAndReply : 'Msg * ?int -> 'Reply

Thanks

like image 933
Okay Avatar asked Feb 29 '12 14:02

Okay


1 Answers

This type signature looks pretty confusing when you see it for the first time, but it does make sense.

The F# library design
The idea behind the is that when you call PostAndReply you need to give it a function that:

  • constructs a message of type 'Msg (to be sent to the agent)
  • after the F# runtime builds a channel for sending messages back to the caller (channels are represented as values of type AsyncReplyChannel<'Reply>).

The message that you construct needs to contain the reply channel, but the F# library does not know how you want to represent your messages (and so it does not know how you want to store the reply channel in the message). As a result, the library asks you to write a function that will construct the message for the agent after the system constructs the channel.

Your alternative suggestion
The problem with your suggestion is that if PostAndReply had a type 'Msg -> 'Reply, the message that the agent receives after it calls Receive would be of the following type:

'Msg * AsyncReplyChannel<'Reply>

... so every message received to the agent would also have to carry a channel for sending replies back. However, you probably don't want to send a reply back for every received message, so this wouldn't really work. Maybe you could use something like:

'Msg * option<AsyncReplyChannel<'Reply>>

... but that's just getting more complicated (and it still isn't quite right, because you can only reply to some messages from 'Msg, but not to all of them).

like image 96
Tomas Petricek Avatar answered Nov 19 '22 09:11

Tomas Petricek