program story

Haskell의 예외 처리

inputbox 2020. 10. 24. 10:13
반응형

Haskell의 예외 처리


세 가지 Haskell 함수의 사용법을 이해하려면 도움이 필요합니다.

  • 시도 ( Control.Exception.try :: Exception e => IO a -> IO (Either e a))
  • 잡기 ( Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a)
  • 핸들 ( Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a)

몇 가지를 알아야합니다.

  1. 언제 어떤 기능을 사용합니까?
  2. 간단한 예제로이 기능을 어떻게 사용합니까?
  3. 캐치와 핸들의 차이점은 무엇입니까? 서로 다른 순서로만 거의 동일한 서명이 있습니다.

나는 내 시련을 적어보고 당신이 나를 도울 수 있기를 바랍니다.

시험

다음과 같은 예가 있습니다.

x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())

두 가지 질문이 있습니다.

  1. 사용자 지정 오류 출력을 어떻게 설정할 수 있습니까?

  2. 모든 오류를 SomeException으로 설정하려면 어떻게해야하나요? :: IO (Either SomeException())

캐치 / 시도

사용자 지정 오류 출력이있는 간단한 예를 보여줄 수 있습니까?


언제 어떤 기능을 사용합니까?

Control.Exception 설명서의 권장 사항은 다음과 같습니다.

  • 예외가 발생한 경우 정리를 수행하려면 finally, bracket또는을 사용하십시오 onException.
  • 예외 후에 복구하고 다른 작업을 수행하려면 try가족 중 하나를 사용하는 것이 가장 좋습니다 .
  • ... 비동기 예외에서 복구하지 않는 한,이 경우 catch또는 catchJust.

try :: 예외 e => IO a-> IO (둘 중 하나)

try소요 IO실행에 조치를, 그리고를 반환합니다 Either. 계산이 성공하면 결과가 Right생성자에 래핑됩니다 . (잘못된 것이 아니라 옳다고 생각하십시오). 작업 에서 지정된 유형 의 예외 발생 하면 Left생성자 에서 반환됩니다 . 예외가 적절한 유형 아닌 경우 스택 위로 계속 전파됩니다. SomeException유형으로 지정 하면 모든 예외가 포착되며 이는 좋은 생각 일 수도 있고 아닐 수도 있습니다.

순수 계산에서 예외를 포착하려면을 사용 evaluate하여 try.

main = do
    result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)
    case result of
        Left ex  -> putStrLn $ "Caught exception: " ++ show ex
        Right val -> putStrLn $ "The answer was: " ++ show val

catch :: 예외 e => IO a-> (e-> IO a)-> IO a

catch와 유사합니다 try. 먼저 지정된 IO작업 을 실행하려고 시도 하지만 예외가 발생하면 처리기에 예외가 주어져 대체 응답을 얻습니다.

main = catch (print $ 5 `div` 0) handler
  where
    handler :: SomeException -> IO ()
    handler ex = putStrLn $ "Caught exception: " ++ show ex

그러나 한 가지 중요한 차이점이 있습니다. catch핸들러를 사용할 비동기 예외 (예 :를 통해 다른 스레드에서 발생)로 인해 중단 될 수 없습니다 throwTo. 비동기 예외를 발생시키려는 시도는 핸들러 실행이 완료 될 때까지 차단됩니다.

catchPrelude 에는 다른 기능 이 있으므로 원하는 작업을 수행 할 수 있습니다 import Prelude hiding (catch).

핸들 :: 예외 e => (e-> IO a)-> IO a-> IO a

handle단순히 catch반대 순서의 인수를 사용합니다. 사용할 코드는 코드를 더 읽기 쉽게 만드는 요소 또는 부분 애플리케이션을 사용하려는 경우 어떤 것이 더 적합한 지에 따라 다릅니다. 그렇지 않으면 동일합니다.

tryJust, catchJust and handleJust

참고로 try, catch그리고 handle잡을 것 모든 지정된 / 추론 유형의 예외. tryJust친구를 사용하면 특별히 처리하려는 예외를 필터링하는 선택기 기능을 지정할 수 있습니다. 예를 들어, 모든 산술 오류는 유형 ArithException입니다. 만 잡으려는 DivideByZero경우 다음을 수행 할 수 있습니다.

main = do
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
    case result of
        Left what -> putStrLn $ "Division by " ++ what
        Right val -> putStrLn $ "The answer was: " ++ show val
  where
    selectDivByZero :: ArithException -> Maybe String
    selectDivByZero DivideByZero = Just "zero"
    selectDivByZero _ = Nothing

순결에 대한 참고 사항

Note that this type of exception handling can only happen in impure code (i.e. the IO monad). If you need to handle errors in pure code, you should look into returning values using Maybe or Either instead (or some other algebraic datatype). This is often preferable as it's more explicit so you always know what can happen where. Monads like Control.Monad.Error makes this type of error handling easier to work with.


See also:


Edward Z. Yang has an article on exception handling in haskell: 8 ways to report errors in Haskell revisited.


Re: question 3: catch and handle are the same (found through hoogle). The choice of which to use will usually depend on the length of each argument. If the action is shorter, use catch and vice versa. Simple handle example from the documentation:

do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...

Also, you could conceivably curry the handle function to make a custom handler, which you could then pass around, eg. (adapted from documentation):

let handler = handle (\NonTermination -> exitWith (ExitFailure 1))

Custom error messages:

do       
    let result = 5 `div` 0
    let handler = (\_ -> print "Error") :: IOException -> IO ()
    catch (print result) handler

I see that one thing that also annoys you (your second question) is the writing of :: IO (Either SomeException ()) and it annoyed me too.

I changed some code now from this:

let x = 5 `div` 0
result <- try (print x) :: IO (Either SomeException ())
case result of
    Left _ -> putStrLn "Error"
    Right () -> putStrLn "OK"

To this:

let x = 5 `div` 0
result <- try (print x)
case result of
    Left (_ :: SomeException) -> putStrLn "Error"
    Right () -> putStrLn "OK"

To do this, you must use the ScopedTypeVariables GHC extension but I think aesthetically it's worth it.

참고URL : https://stackoverflow.com/questions/6009384/exception-handling-in-haskell

반응형