Haskell中的exception处理
我需要帮助来理解三个Haskell函数的用法
- 尝试(
Control.Exception.try :: Exception e => IO a -> IO (Either ea)
) - catch(
Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a
) - handle(
Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a
)
我需要知道几件事情:
- 我什么时候使用哪个function?
- 如何用一个简单的例子来使用这个函数?
- 捕获和处理的区别在哪里? 他们有几乎相同的签名只有一个不同的顺序。
我会尽力写下我的试炼,希望你能帮助我:
尝试
我有一个例子:
x = 5 `div` 0 test = try (print x) :: IO (Either SomeException ())
我有两个问题:
-
我如何设置自定义的错误输出?
-
我能做些什么来设置所有的错误SomeException,所以我不必写
:: IO (Either SomeException())
捕获/试
你能告诉我一个自定义错误输出的简短例子吗?
我什么时候使用哪个function?
以下是Control.Exception文档的build议:
- 如果您想在发生exception时进行一些清理,请使用
finally
,bracket
或onException
。 - 要在exception之后恢复并做其他事情,最好的select是使用其中一个
try
系列。 - …除非你正在从asynchronousexception恢复,在这种情况下使用
catch
或catchJust
。
try :: Exception e => IO a – > IO(ea)
try
一个IO
操作来运行,并返回一个Either
。 如果计算成功,结果被包装在一个Right
构造函数中。 (认为正确而不是错误)。 如果操作抛出了指定types的exception,则返回到Left
构造函数中。 如果exception不是合适的types,它将继续传播堆栈。 指定SomeException
作为types将捕获所有exception,这可能是也可能不是一个好主意。
请注意,如果要从纯计算中捕获exception,则必须使用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 :: Exception e => IO a – >(e – > IO a) – > IO a
catch
类似的try
。 它首先尝试运行指定的IO
操作,但是如果抛出exception,处理程序将被赋予exception以获得替代答案。
main = catch (print $ 5 `div` 0) handler where handler :: SomeException -> IO () handler ex = putStrLn $ "Caught exception: " ++ show ex
但是,有一个重要的区别。 当使用catch
处理程序不能被exceptionexception(即通过throwTo
从另一个线程抛出)中断。 试图引发exceptionexception会阻止,直到处理程序完成运行。
请注意,在Prelude中有一个不同的catch
,所以你可能想要执行import Prelude hiding (catch)
。
handle :: Exception e =>(e – > IO a) – > IO a – > IO a
handle
只是以相反的顺序catch
到参数。 使用哪一个取决于什么使得你的代码更具可读性,或者哪一个更适合你,如果你想使用部分应用程序。 他们是相同的。
尝试,抓住,处理,正义
请注意, try
, catch
和handle
会捕获指定/推断types的所有exception。 tryJust
和朋友允许你指定一个select器函数来筛选出你特别想处理的exception。 例如,所有的算术错误都是ArithException
types的。 如果你只想抓到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
关于纯度的说明
请注意,这种types的exception处理只能发生在不纯的代码(即IO
monad)中。 如果您需要处理纯代码中的错误,则应该使用Maybe
或Either
(或其他代数数据types)来查看返回的值。 这通常是可取的,因为它更明确,所以你总是知道在哪里可以发生。 Monad像Control.Monad.Error
使这种types的error handling更容易处理。
也可以看看:
- Control.Exception
Edward Z. Yang在haskell上有一篇关于exception处理的文章: Haskell重新报告错误的8种方法 。
回复:问题3:抓住和处理是相同的 (通过hooglefind)。 select哪一个通常取决于每个参数的长度。 如果动作较短,则使用catch,反之亦然。 文档中的简单句柄示例:
do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...
此外,你可以想象咖喱手柄function,使自定义处理程序,然后你可以传递,例如。 (改编自文件):
let handler = handle (\NonTermination -> exitWith (ExitFailure 1))
自定义错误消息:
do let result = 5 `div` 0 let handler = (\_ -> print "Error") :: IOException -> IO () catch (print result) handler
我看到有一件事情也使你恼火(你的第二个问题)是:: IO (Either SomeException ())
,也使我恼火。
我现在改变了一些代码:
let x = 5 `div` 0 result <- try (print x) :: IO (Either SomeException ()) case result of Left _ -> putStrLn "Error" Right () -> putStrLn "OK"
对此:
let x = 5 `div` 0 result <- try (print x) case result of Left (_ :: SomeException) -> putStrLn "Error" Right () -> putStrLn "OK"
要做到这一点,你必须使用ScopedTypeVariables
GHC扩展,但我认为这是值得的。