2015-01-02 1 views
2

MailboxProcessor<'Msg> 클래스를 통해 F #의 상담원과 작업을 시작하려고하는데 예외에 대한 적절한 처리가 없다는 것을 알게되었습니다. 하스켈 세계에서는 어떤 예외도 없으므로 문제를 처리하는 적절한 방법은 단순히 응답 사례로 제공하는 것입니다. 다음 에이전트의 .PostAndReply 메소드를 호출하고 InvalidData 데이터가 유효하지 않은 이유를 나타내는 메시지를 포함 얻을 수실패 후 MailboxProcessor를 다시 시작 하시겠습니까?

type AgentResponse = 
    | HandledData of string 
    | InvalidData of string 

하나 : 그래서 에이전트는 같은 회신 할 수있다. 그러나 이것은 하스켈이 아니며 때때로 예외가 발생합니다.

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에 배치하는 것 외에도 예외 발생 후 에이전트를 계속 실행하는 좋은 방법이 있습니까? 다른 방법으로, 이것을 다루는 "표준"방법이 누군가가 나를 가리킬 수 있도록 설립 되었습니까? .... ghci에서 Data.List.head []을 시도

불행하게도 종속 유형의 부족을 의미, 우리가 어떤 계산 의미가 없다 타입 올바른 코드를 쓸 수 있는지, 하스켈 또는 F에 # :

+0

나는 MailboxProcessor (에이전트)의 모든 아이디어가 실패 할 때 죽는다 고 생각한다. 어쩌면 다른 에이전트를 대기열로 사용할 수 있고 오류가있을 때 대기열 에이전트가 새 에이전트를 실행하는 곳에 'TryPostAndReply'를 사용할 수 있습니까? 고성능이 필요하다면'try ... catch'가 아마도 최선의 방법이라고 생각합니다. – mydogisbox

+1

@mydogisbox 모든 것을 잡는 것은 최적이 아닌 아이디어처럼 보입니다. 그러나 나는 또한 이것이 최선의 방법이라고 생각합니다. 내가 피하려고하는 문제는 대기중인 메시지의 복사본 두 개를 유지해야한다는 것입니다. 예를 들어 F #에 메일러 데몬을 작성한 경우 데몬은 에이전트가 잡을 생각이없는 네트워크 예외를 쳤기 때문에 대기중인 전자 메일을 "잃어 버려서는 안됩니다."따라서 메시지는 대기열뿐만 아니라 다른 곳에 보관해야합니다.대기열에있는 모든 항목의 두 번째 사본이 필요하면 대기열이있는 상담원의 목적을 무력화하는 것처럼 느껴집니다. – Amazingant

+0

동의합니다. erlang 에이전트 모델이이 문제를 어떻게 처리하는지 궁금합니다. – mydogisbox

답변

3

당신은 하스켈에서 예외가 않습니다.

실제로는 try ... with 블록을 사용하여 예외를 처리하는 것은 좋지 않습니다. 당신은 몸 전체를 감쌀 필요는 없으며 코드의 순수하지 않은 부분 만 감쌀 수 있습니다.

그러면 고전적으로 적절한 생성자를 사용하여 만든 값을 반환합니다. ,이 시작하고 정상적인 MailboxProcessor처럼 실행할 수 있습니다

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 

:

+1

"순수한 코드의 일부"만 .NET에 있으면 그 모든 것이되지 않을까요? :) 순수 키워드를 기다리면서 원하는 것이 생길 것입니다 : https://fslang.uservoice.com/forums/245727-f-language/suggestions/5670335-pure-functions-pure-keyword – Amazingant

+1

당신은 C로 순수한 코드를 작성할 수 있습니다. – nicolas

+0

아마,하지만 그걸로 unenforced 순수한 코드; 그 시점에서, 하스켈과 같은 언어가있는 이유는 무엇입니까? 게다가 BCL (및 제 3 자 라이브러리)의 어떤 객체가 순수한 방식으로 사용하기에 안전한지 결정하는 것은 다소 어렵습니다. – Amazingant

2

한 가지 가능한 옵션은 반복 에이전트의 몸을 실행 this question에서 토마스 Petricek 이상에 의해 정의 된 HandlingMailbox 유사한 도우미 유형입니다 제공된 에이전트 본문에서 예외가 발생하면 자체적으로 다시 실행됩니다.

편집 : MailboxProcessor 내부 블록을 while true do.. 블록과 비교하여 재귀 함수를 사용하도록 변경하십시오. 이전 코드는 대상 함수가 정상적으로 반환 될 때 실행을 멈추지 않습니다.

관련 문제