2012-02-29 38 views
12

综观:MailboxProcessor.PostAndReply设计选择

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

我想不通,为什么签名看起来很反直觉我。我们想要做的是向代理发布消息,并等待回复。为什么我们不得不给他一个奇怪的功能作为'消息'?

再看到这个MSDN片段:

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

我宁愿喜欢的东西是这样的:

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

感谢

回答

9

当你看到它的这种类型的签名看起来很混乱第一次,但它确实有道理。

的F#库设计
背后的想法是,当你调用PostAndReply你需要给它一个功能是:

  • 构建'Msg类型的消息(发送到代理)
  • 在F#运行时建立一个发送消息回到调用者的通道(通道表示为AsyncReplyChannel<'Reply>类型的值)。

你构建需要包含回复通道的信息,但F#库不知道你是怎么想代表你的消息(并因此它不知道你是怎么想存储在邮件中回复道)。因此,库会要求您编写一个函数,在系统构建通道后为函数构造消息。

您的替代建议
你的建议的问题是,如果PostAndReply有型'Msg -> 'Reply,该代理接收调用后的消息Receive将是以下类型:

'Msg * AsyncReplyChannel<'Reply> 

。 ..所以收到代理的每条消息都必须携带一个发送回复的通道。但是,您可能不希望为每个收到的邮件发送回复,所以这不会起作用。也许你可以使用类似:

'Msg * option<AsyncReplyChannel<'Reply>> 

...但是这只是越来越复杂(它仍然是不完全正确的,因为你只能从'Msg回复一些邮件,而不是所有的人)。

+0

好的,谢谢,我明白了。如果是我,我会在构造函数中需要一个额外的参数:一个函数,该函数从AsyncReplyChannel('AsyncReplyChannel <'Reply> - >'Msg')构造一个消息,并附加到代理的主体。事实上,从用户为中心的角度来看,我不明白为什么人们会在调用'PostAndReply'时引入不同的方式来构建来自回复通道的msg,尽管它不是通用的。 – Okay 2012-02-29 15:50:32

+2

@Okay - 这也行不通。有不同的方式来构建包含'AsyncReplyChannel'的'Msg'值是很常见的。例如,阻塞队列代理(请参阅MSDN http://msdn.microsoft.com/en-us/library/hh297096.aspx)具有两个不同的消息,这两个消息都携带一个回复通道,因此您有两种构建消息的方法。 这就是说,我同意签名很混乱。有更多可读的替代方案会很好,但我不能认为这可能是... – 2012-02-29 16:29:49