3

我正在尝试通过该类开始使用 F# 中的代理,但MailboxProcessor<'Msg>我很快意识到我没有对异常进行适当的处​​理。在 Haskellian 世界中,不会有任何例外,因此处理问题的正确方法是简单地将它们作为响应案例提供;因此代理可以回复如下内容:

type AgentResponse =
    | HandledData of string
    | InvalidData of string

然后可以调用代理的.PostAndReply方法并获得InvalidData包含指示数据无效原因的消息。但是,这不是 Haskell,有时会发生异常。所以如果我这样做:

let agent =
    new MailboxProcessor<string * AsyncReplyChannel<AgentResponse>>(fun inbox ->
        async {
            while true do
                let! (msg, replyChannel) = inbox.Receive()
                if msg = "die" then failwith "Unknown exception encountered!"
                else replyChannel.Reply(HandledData(msg))
        })

agent.Start()
agent.PostAndReply (fun rc -> "die", rc)
agent.PostAndReply (fun rc -> "Test", rc)

第二次调用agent.PostAndReply无限期阻塞。当不使用AsyncReplyChannel并因此只是调用agent.Post时,调用不会阻塞,但是一旦代理遇到异常,新消息就会留在队列中。在任何一种情况下重新启动代理似乎都是不可能的,因为该agent.Start函数InvalidOperationException在再次调用时返回一个,处理这个问题的自然方法似乎是创建一个具有干净状态的新代理,但随后所有排队的消息都丢失了。

除了将代理的整个主体包装在 中之外try..with,还有什么好方法可以让代理在异常后继续运行?或者,是否已经建立了一种“标准”的处理方式,有人可以指出我?

4

2 回答 2

3

你在 Haskell 中确实有例外:Data.List.head []在 ghci 中尝试......

不幸的是,缺乏依赖类型意味着,在 Haskell 或 F# 中,我们可以编写类型正确的代码,但没有计算意义。

实际上,用块包裹起来try ... with对于处理异常并不是一个坏主意。你不需要包装整个身体,只是你的代码的非纯部分。

然后经典地,您返回一个使用适当的构造函数生成的值。

于 2015-01-02T15:55:16.153 回答
2

一个可能的选项是类似于HandlingMailboxTomas Petricek 在这个问题上定义的辅助类型,它重复运行代理的主体:

type ResilientMailbox<'T> private(f:ResilientMailbox<'T> -> Async<unit>) as self =
    let event = Event<_>()
    let inbox = new MailboxProcessor<_>(fun _inbox ->
        let rec loop() = async {
            try
                return! f self
            with e ->
                event.Trigger(e)
                return! loop()
            }
        loop())
    member __.OnError = event.Publish
    member __.Start() = inbox.Start()
    member __.Receive() = inbox.Receive()
    member __.Post(v:'T) = inbox.Post(v)
    static member Start(f) =
        let mbox = new ResilientMailbox<_>(f)
        mbox.Start()
        mbox

这可以像正常一样启动和运行MailboxProcessor,但如果提供的代理主体抛出异常,它将重新运行。

编辑:更改内部MailboxProcessor以使用递归函数而不是while true do..块;当目标函数正常返回时,前面的代码不会停止运行。

于 2015-01-02T16:00:43.963 回答