12

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

Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
Okay
  • 224
  • 1
  • 9

1 Answers1

9

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

Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Ok thanks, I see. If it was me, I would have required in the constructor an additional parameter: a function that constructs a message from an AsyncReplyChannel (`AsyncReplyChannel<'Reply> -> 'Msg`) additionnally to the body of the agent. Indeed, from a user-centric point of view, I fail to see why would people want to introduce different ways to construct a msg from a reply channel, when calling `PostAndReply`, though it would be less generic. – Okay Feb 29 '12 at 15:50
  • 2
    @Okay - That would not work either. It is quite common to have different ways of constructing `'Msg` values that contain `AsyncReplyChannel`. For example, the blocking queue agent (see MSDN http://msdn.microsoft.com/en-us/library/hh297096.aspx) has two different messages that both carry a reply channel and so you have two ways of constructing the message. That said, I agree that the signature is pretty confusing. It would be nice to have more readable alternative, but I just cannot think what that might be... – Tomas Petricek Feb 29 '12 at 16:29