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

Eug*_*Loy 6 haskell exception-handling exception

我正在播放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
Run Code Online (Sandbox Code Playgroud)

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

然后我定义main将表达式try包装进(将其包装到IO第一个,因为try接受IO)然后将其从IO(由...生成try)和print它展开.

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

Right *** Exception: Prelude.read: no parse
Run Code Online (Sandbox Code Playgroud)

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

module Main where

import Control.Exception

thrower = (read "A") :: Int

main = do
    x <- (try $ return thrower) :: IO (Either SomeException Int)
    print x
Run Code Online (Sandbox Code Playgroud)

...它被异常崩溃:

haskelltest.exe: Prelude.read: no parse
Run Code Online (Sandbox Code Playgroud)

好像异常滑倒了try.

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

lef*_*out 9

好吧,基本上(正如Sebastian Redl先前指出的那样)这是一个严格的问题.return thrower不以任何方式评估thrower,所以try 成功.的内容只有当Either SomeException Int打印,即Right thrower,没有read真正尝试解析"A",而失败......但在这一点上,try已经结束了.

防止这种情况的方法是将解析结果严格地注入IOmonad中

main = do
    x <- try $ evaluate thrower :: IO (Either SomeException Int)
    print x
Run Code Online (Sandbox Code Playgroud)

为什么try在GHCi中你的代码失败我不知道; 我敢说它不应该.啊哈:正如里德所说,它实际上并没有失败!

可以说,这是为什么在Haskell中通常应该避免异常的一个例子.使用合适的monad变换器来明确可能发生的错误,并获得对错误检查的可靠评估.


Rei*_*ton 7

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

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