我需要帮助来理解三个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
)我需要知道几件事:
我会写下我的试验,希望你能帮助我:
尝试
我有一个例子:
x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())
我有两个问题:
如何设置自定义错误输出?
如何将所有错误设置为SomeException,以便我不必编写:: IO (Either SomeException())
捕获/尝试
您能告诉我一个带有自定义错误输出的简短示例吗?
答案 0 :(得分:122)
以下是Control.Exception文档的建议:
finally
,bracket
或onException
。try
系列之一。catch
或catchJust
。 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
与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
只是catch
,其参数的顺序相反。使用哪一个取决于使您的代码更具可读性的原因,或者如果您想使用部分应用程序,哪一个更适合您。它们在其他方面是相同的。
请注意,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
这样的Monads使这种类型的错误处理更容易使用。
另见:
答案 1 :(得分:5)
Edward Z. Yang在haskell中有一篇关于异常处理的文章:8 ways to report errors in Haskell revisited。
答案 2 :(得分:1)
回复:问题3:捕获和处理是same(通过hoogle找到)。选择使用哪个通常取决于每个参数的长度。如果操作较短,请使用catch,反之亦然。文档中的简单句柄示例:
do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...
另外,你可以想象一下咖喱手柄功能来制作一个自定义处理程序,你可以传递它,例如。 (改编自文件):
let handler = handle (\NonTermination -> exitWith (ExitFailure 1))
自定义错误消息:
do
let result = 5 `div` 0
let handler = (\_ -> print "Error") :: IOException -> IO ()
catch (print result) handler
答案 3 :(得分:1)
我发现有一件事也让你烦恼(你的第二个问题)是:: 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扩展,但我认为在美学上它是值得的。