MailboxProcessor.PostAndReply 的设计选择

12

看一下:

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

感谢

1个回答

9

第一次看到这个类型签名时,可能会感到困惑,但它确实是有意义的。

F#库设计
这个想法是,当您调用 PostAndReply 时,需要给它一个函数,该函数:

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

您构建的消息需要包含回复通道,但是 F# 库不知道您希望如何表示您的消息(因此不知道您希望如何在消息中存储回复通道)。因此,该库要求您编写一个函数,在系统构建通道后,将构造代理的消息。

您提出的替代建议
您的建议存在问题,即如果 PostAndReply 具有类型 'Msg -> 'Reply,则代理在调用 Receive 后收到的消息将是以下类型:

'Msg * AsyncReplyChannel<'Reply>

每个发给代理的消息都必须携带一个通道以便回复。但是,您可能并不想为每个收到的消息都发送回复,因此这种方法并不实际。也许您可以尝试使用以下方法:

'Msg * option<AsyncReplyChannel<'Reply>>

...但这只是变得更加复杂(而且仍然不完全正确,因为您只能回复一些信息从'Msg',而不是所有的)。


好的,谢谢,我明白了。如果是我,我会在构造函数中要求一个额外的参数:一个从AsyncReplyChannel(AsyncReplyChannel<'Reply> -> 'Msg)构造消息的函数,除了代理的主体之外。实际上,从用户中心的角度来看,我不明白为什么人们希望引入不同的方式从回复通道构造消息,当调用PostAndReply时,虽然它会更少见。 - Okay
2
@Okay - 这也不行。构造包含AsyncReplyChannel'Msg值的方式有很多种。例如,阻塞队列代理(请参见MSDN http://msdn.microsoft.com/en-us/library/hh297096.aspx)有两个不同的消息都携带回复通道,因此您有两种构造消息的方式。话虽如此,我同意签名非常令人困惑。拥有更易读的替代方案会很好,但我无法想出可能的解决方案... - Tomas Petricek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接