finally并且onException是模块中的两个函数Control.Exception,它们具有相同的签名但行为不同.
这是文件.因为finally,它说:
finally
:: IO a -- computation to run first
-> IO b -- computation to run afterward (even if an exception was raised)
-> IO a
Run Code Online (Sandbox Code Playgroud)
虽然因为onException,它说:
喜欢
finally,但只有在计算引发异常时才执行最终操作.
所以我做了以下测试:
ghci> finally (return $ div 4 2) (putStrLn "Oops!")
Oops!
2
ghci> finally (return $ div 4 0) (putStrLn "Oops!")
Oops!
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)
这是按预期行事的.
但是,onException不是:
ghci> onException (return $ div 4 2) (putStrLn "Oops!")
2
ghci> onException (return $ div 4 0) (putStrLn "Oops!") -- does not act as expected
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)
如上所述,onException仅在引发异常时才执行最终操作,但上面的示例显示onException不执行最终操作,即putStrLn "Oops!"引发异常时.
检查后的源代码的onException,我尝试测试如下:
ghci> throwIO (SomeException DivideByZero) `catch` \e -> do {_ <- putStrLn "Oops!"; throwIO (e :: SomeException)}
Oops!
*** Exception: divide by zero
ghci> onException (throwIO (SomeException DivideByZero)) (putStrLn "Oops!")
Oops!
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)
可以看出,当明确提出异常时,执行了最终操作.
所以这个问题return $ div 4 0确实引发了异常,但为什么onException (return $ div 4 0) (putStrLn "Oops!")不执行最终操作putStrLn "Oops!"呢?我错过了什么?以及如何执行异常?
ghci> throwIO (SomeException DivideByZero)
*** Exception: divide by zero
ghci> (return $ div 4 0) :: IO Int
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)
Ale*_*ing 10
你被懒惰的评价所困扰.
其中一个关键保证throwIO是它可以保证何时在执行其他IO操作时引发异常.从文档:
虽然
throwIO有一个类型为类型的实例,但这throw两个函数略有不同:Run Code Online (Sandbox Code Playgroud)throw e `seq` x ===> throw e throwIO e `seq` x ===> x第一个例子将引发异常
e,而第二个例子则不会.实际上,throwIO只有在IOmonad中使用时才会引发异常.该throwIO变种应优先使用扔内抛出一个异常IO,因为它保证相对于其他订购的单子IO操作,而throw不会.
这意味着,当throwIO e执行动作(不仅仅是评估!)作为执行动作的执行的一部分时onException,保证实际引发异常.由于异常是在异常处理程序的执行的动态范围内引发的,因此检测到异常,并执行处理程序函数.
但是,当您编写时return e,它生成的操作在执行时不会评估e为WHNF,并且e仅在/当操作的结果本身被评估时才进行评估.当div 4 0GHCi通过show动作的结果强制表达式时,控件已经离开执行的动态范围onException,并且它安装的处理程序不再在堆栈上.提出异常,但提出的时间太晚了.
为了获得您想要的行为,确保您div 4 0作为行动执行的一部分进行评估至关重要,而不是在行动之前或之后.这是在evaluate从功能Control.Exception是.它将其对WHNF的参数作为执行IO动作本身的一部分进行评估,保证作为该评估的一部分引发的任何异常都将被周围的异常处理程序检测到:
ghci> onException (evaluate $ div 4 0) (putStrLn "Oops!")
Oops!
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)
道德:在处理Haskell中的异常时,要非常小心何时对事物进行求值以确保在异常处理程序的动态范围内引发异常,并且不会因延迟评估而延迟.