我正在播放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
。
我在这里缺少什么,处理这个问题的正确方法是什么?
答案 0 :(得分:9)
嗯,基本上(as Sebastian Redl pointed out earlier)这是一个严格的问题。 return thrower
不会以任何方式评估thrower
,因此try
成功。只有在打印Either SomeException Int
的内容时,即Right thrower
,read
实际上会尝试解析"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中是无缓冲的,但默认情况下在编译的程序中缓冲行。