使用`try`处理纯代码抛出的异常

时间:2015-07-05 14:45:59

标签: haskell exception exception-handling

我正在播放haskell中的异常,偶然发现了一件我无法理解的事情。

在GHCi我做:

Prelude Control.Exception> let thrower = (read "A") :: Int
Prelude Control.Exception> :{
Prelude Control.Exception| let main = do
Prelude Control.Exception|     x <- (try $ return thrower) :: IO (Either SomeException Int)
Prelude Control.Exception|     print x
Prelude Control.Exception| :}
Prelude Control.Exception> main

这定义了thrower,我的测试表达式会因异常而失败。

然后我定义main将该表达式包装到try中(首先将其包装到IO中,因为try接受IO)然后将其展开IO(由try生成)和print生成。

到目前为止,一切看起来都很棒 - 在repl中评估main会将异常包含在以下内容中:

Right *** Exception: Prelude.read: no parse

但是,如果我尝试编译并执行与app相同的代码:

module Main where

import Control.Exception

thrower = (read "A") :: Int

main = do
    x <- (try $ return thrower) :: IO (Either SomeException Int)
    print x

......它因异常而崩溃:

haskelltest.exe: Prelude.read: no parse

似乎异常滑过try

我在这里缺少什么,处理这个问题的正确方法是什么?

2 个答案:

答案 0 :(得分:9)

嗯,基本上(as Sebastian Redl pointed out earlier)这是一个严格的问题。 return thrower不会以任何方式评估thrower,因此try 成功。只有在打印Either SomeException Int的内容时,即Right throwerread实际上会尝试解析"A",并且失败......但此时try IO 1}}已经结束了。

防止这种情况的方法是inject the parse result strictly进入main = do x <- try $ evaluate thrower :: IO (Either SomeException Int) print x monad,

try

为什么{{1}}在GHCi中使用您的代码失败我不知道;我敢说它不应该。 啊哈:as Reid noted它实际上并没有失败

可以说,这是为什么在Haskell中通常应该避免异常的一个例子。 Use a suitable monad transformer明确可能发生的错误,并对错误检查进行可靠的评估。

答案 1 :(得分:7)

您遗失的部分原因是,当您在ghci中运行代码时,try也无法捕获read "A" :: Int引发的错误。你不期待Left <something>结果吗?请参阅leftaroundabout的答案,了解其原因。

这里ghci和ghc之间的区别可能是由于输出缓冲:输出到stdout(如此处的"Right ")在ghci中是无缓冲的,但默认情况下在编译的程序中缓冲行。