10

하스켈에서 Scheme 인터프리터 용 REPL을 구현 중이며 UserInterrupt, StackOverflow, HeapOverflow 등과 같은 비동기 이벤트를 처리하고 싶습니다. 기본적으로 한 가지 예외가 예상대로 작동하스켈에서 UserInterrupt 예외 처리하기

repl evaluator = forever $ (do 
     putStr ">>> " >> hFlush stdout 
     out <- getLine >>= evaluator 
     if null out 
      then return() 
      else putStrLn out) 
     `catch` 
     onUserInterrupt 

    onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption" 
    onUserInterrupt e = throw e 

    main = do 
     interpreter <- getMyLispInterpreter 
     handle onAbort (repl $ interpreter "stdin") 
     putStrLn "Exiting..." 

    onAbort e = do 
     let x = show (e :: SomeException) 
     putStrLn $ "\nAborted: " ++ x 

을 : UserInterrupt가 발생하고 HeapOverflow StackOverflow의 발생시 등, 적절한 메시지를 인쇄 할 때 다음과 같이 계산 전류 I이 구현. 통역사를 시작하고 Ctrl-Z + Enter를 누르면 다음과 같이 나타납니다.

>>> ^Z 

    Aborted: <stdin>: hGetLine: end of file 
    Exiting... 

맞습니다. 하지만 Ctrl 키-Z + 내가 얻을 입력 한 다음 인터프리터하고 Ctrl-C 시작하는 경우 :

>>> 
    UserInterruption 
    >>> ^Z 

를 그리고이 정지하고 난 더 이상 인터프리터를 사용할 수 없습니다. 그러나 Ctrl-C를 다시 누르면 REPL이 차단을 해제합니다. 나는 많은 것을 수색했고 나는 그것의 이유를 이해할 수 없다. 아무도 날 설명 할 수 있을까?

감사합니다.

+0

절대로 Ctrl-Z가 표시되지 않는 것처럼 캡처가 발생할 때마다 예외를 다시 발생시켜야합니다. 붙 잡히다. 첫 번째 Ctrl-C는 캡처되지만 두 번째는 캡처되지 않습니다. 아마 같은 문제 일 겁니다. 완전한 테스트 케이스에서 코드를 변경할 수 있습니까? F.e. '통역사'대신 '반송'을하고 적절한 수입을 추가하십시오. –

답변

10

제어-C 처리는 catch 작동하지 않습니다 GHC#2301: Proper handling of SIGINT/SIGQUIT 여기

관련이있을 수 있습니다 것이 evaluator 제거와 함께 작업 테스트 케이스이다

리눅스, 제어-Z가에
module Main where 

import Prelude hiding (catch) 

import Control.Exception (SomeException(..), 
          AsyncException(..) 
         , catch, handle, throw) 
import Control.Monad (forever) 
import System.IO 

repl :: IO() 
repl = forever $ (do 
    putStr ">>> " >> hFlush stdout 
    out <- getLine 
    if null out 
     then return() 
     else putStrLn out) 
    `catch` 
    onUserInterrupt 

onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption" 
onUserInterrupt e = throw e 

main = do 
    handle onAbort repl 
    putStrLn "Exiting..." 

onAbort e = do 
    let x = show (e :: SomeException) 
    putStrLn $ "\nAborted: " ++ x 

Sjoerd가 언급 한대로 잡히지 않았습니다. 아마도 당신은 Control-Z가 EOF에 사용되는 Windows에있을 것입니다. 우리는 복제 제어-D, 당신이 본 행동와 리눅스에 EOF 신호를 보낼 수 :

>>> ^D 
Aborted: <stdin>: hGetLine: end of file 
Exiting... 

EOF는 당신의 handle/onAbort 기능에 의해 처리되며, 제어-C는 catch/onUserInterrupt에 의해 처리됩니다. 여기서 문제는 repl 함수가 첫 번째 Control-C 만 잡을 수 있다는 것입니다. handle/onAbort 함수를 제거하여 테스트 케이스를 단순화 할 수 있습니다. 위에서 언급 한 것처럼 Control-C 처리가 catch과 작동하지 않으면 GHC#2301: Proper handling of SIGINT/SIGQUIT과 관련이있을 수 있습니다.

다음 버전 대신 제어-C에 대한 지속적인 신호 처리기를 설치할 수는 POSIX API를 사용

>>> ^C 
keyboardSignal 

>>> ^C 
keyboardSignal 

>>> ^C 
keyboardSignal 

그렇지 않은 경우 : 제어-CS를 여러 번 누르는 처리 할 수 ​​

module Main where 

import Prelude hiding (catch) 

import Control.Exception (SomeException(..), 
          AsyncException(..) 
         , catch, handle, throw) 
import Control.Monad (forever) 
import System.IO 
import System.Posix.Signals 

repl :: IO() 
repl = forever $ do 
    putStr ">>> " >> hFlush stdout 
    out <- getLine 
    if null out 
     then return() 
     else putStrLn out 

reportSignal :: IO() 
reportSignal = putStrLn "\nkeyboardSignal" 

main = do 
    _ <- installHandler keyboardSignal (Catch reportSignal) Nothing 
    handle onAbort repl 
    putStrLn "Exiting..." 

onAbort e = do 
    let x = show (e :: SomeException) 
    putStrLn $ "\nAborted: " ++ x 

Posix API를 사용하여 Windows에 영구 신호 처리기를 설치하려면 http://suacommunity.com/dictionary/signals.php