3

クラスを介して F# でエージェントの操作を開始しようとしてMailboxProcessor<'Msg>いますが、例外を適切に処理していないことにすぐに気付きました。Haskelly の世界では、例外は存在しないため、問題を処理する適切な方法は、単純に応答ケースとして提供することです。そのため、エージェントは次のように返信できます。

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)

2 回目の呼び出しは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

考えられるオプションの 1 つは、この質問HandlingMailboxで Tomas 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 に答える