什么时候使用哪个功能?
这是来自 Control.Exception 文档的建议:
- 如果您想在引发异常时进行一些清理,请使用
finally、bracket 或onException。
- 要在异常后恢复并执行其他操作,最好的选择是使用
try 系列之一。
- ...除非您正在从异步异常中恢复,在这种情况下使用
catch 或catchJust。
try :: Exception e => IO a -> IO (Either e a)
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 从另一个线程抛出)。尝试引发异步异常将阻塞,直到您的处理程序完成运行。
请注意,Prelude 中有一个不同的catch,因此您可能需要使用import Prelude hiding (catch)。
handle :: Exception e => (e -> IO a) -> IO a -> IO a
handle 只是 catch,参数顺序相反。使用哪一种取决于使您的代码更具可读性的原因,或者如果您想使用部分应用程序,哪一种更适合。它们在其他方面是相同的。
tryJust、catchJust 和 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
关于纯度的说明
请注意,这种类型的异常处理只能发生在不纯代码中(即IO monad)。如果您需要处理纯代码中的错误,您应该使用Maybe 或Either(或其他一些代数数据类型)来查看返回值。这通常更可取,因为它更明确,因此您始终知道在哪里会发生什么。像 Control.Monad.Error 这样的 Monad 使这种类型的错误处理更容易使用。
另见: