MailboxProcessor.PostAndReply设计选择

Oka*_*kay 12 f# agents mailboxprocessor

看着:

member this.PostAndReply : (AsyncReplyChannel<'Reply> -> 'Msg) * ?int -> 'Reply
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚为什么签名看起来对我来说非常直观.我们想要做的是向代理发布消息,然后等待回复.为什么我们必须给他一个奇怪的功能作为'信息'?

再次看到这个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()
Run Code Online (Sandbox Code Playgroud)

我更喜欢这样的东西:

member this.PostAndReply : 'Msg * ?int -> 'Reply
Run Code Online (Sandbox Code Playgroud)

谢谢

Tom*_*cek 9

当你第一次看到它时,这种类型的签名看起来很混乱,但它确实有意义.

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

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

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

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

'Msg * AsyncReplyChannel<'Reply>
Run Code Online (Sandbox Code Playgroud)

...因此,收到代理的每条消息都必须携带一个回复发送回复的频道.但是,您可能不希望为每个收到的消息发送回复,因此这不会真正起作用.也许你可以使用类似的东西:

'Msg * option<AsyncReplyChannel<'Reply>>
Run Code Online (Sandbox Code Playgroud)

...但这只是变得越来越复杂(它仍然不太正确,因为你只能回复来自'Msg但不是所有的消息).

  • @Okay - 那也行不通.使用不同的方法构造包含`AsyncReplyChannel`的'Msg`值是很常见的.例如,阻塞队列代理(请参阅MSDN http://msdn.microsoft.com/en-us/library/hh297096.aspx)有两个不同的消息都带有回复通道,因此您有两种构造消息的方法.也就是说,我同意签名非常令人困惑.拥有更多可读替代品会更好,但我想不出那可能是什么...... (2认同)