代码之家  ›  专栏  ›  技术社区  ›  Z-Y.L

“onException”的行为

  •  4
  • Z-Y.L  · 技术社区  · 6 年前

    finally onException Control.Exception ,具有相同的签名,但行为不同。 Here 是文档。 最后

    finally 
    :: IO a  -- computation to run first
    -> IO b  -- computation to run afterward (even if an exception was raised)
    -> IO a
    

    ,而 一个例外 ,上面写着:

    最后 ,但仅当存在 计算引发的异常。

    ghci> finally (return $ div 4 2) (putStrLn "Oops!")
    Oops!
    2
    ghci> finally (return $ div 4 0) (putStrLn "Oops!")
    Oops!
    *** Exception: divide by zero
    

    一切如期而至。

    然而, 一个例外 不会:

    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
    

    如上所述, 仅在引发异常时执行最终操作,但上面的示例显示 不执行最终操作,即。 putStrLn "Oops!" 当出现异常时。

    source code 对于 ,我尝试以下测试:

    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
    

    可以看出,当显式引发异常时,执行了最后的操作。

    所以问题是 return $ div 4 0 确实抛出了一个异常,但是 onException (return $ div 4 0) (putStrLn "Oops!") 不执行最终操作

    ghci> throwIO (SomeException DivideByZero)
    *** Exception: divide by zero
    ghci> (return $ div 4 0) :: IO Int
    *** Exception: divide by zero
    
    1 回复  |  直到 6 年前
        1
  •  10
  •   Community CDub    4 年前

    你被懒惰的评价咬了一口。

    关键保证之一 throwIO 它能保证 在执行其他合同时,将提出例外情况 IO 行动。从 the documentation

    尽管 throw ,这两种功能有着微妙的区别:

    throw e   `seq` x  ===> throw e
    throwIO e `seq` x  ===> x
    

    第一个示例将导致异常 e throwIO公司 只会导致在中使用异常时引发异常 throwIO公司 木卫一 木卫一 没有。

    这意味着,当 throwIO e onException ,它保证实际引发异常。由于异常是在异常处理程序执行的动态范围内引发的,因此会检测到异常并执行处理程序函数。

    然而,当你写 return e ,它产生的动作 电子 执行时发送给WHNF,以及 仅当操作结果本身被评估时才被评估。等到 div 4 0 show 通过改变操作结果,控件保留了执行的动态范围 一个例外 ,并且它安装的处理程序不再在堆栈上。引发了异常,但引发得太晚。

    为了得到你想要的行为,关键是要确保你的评估 分区4 0 作为你行动执行的一部分,而不是前后一刻。这是什么 the evaluate function from Control.Exception 是给你的。它将其参数评估为WHNF,作为执行 木卫一 操作本身,保证作为该评估的一部分引发的任何异常都将被周围的异常处理程序检测到:

    ghci> onException (evaluate $ div 4 0) (putStrLn "Oops!")
    Oops!
    *** Exception: divide by zero
    

    寓意:在Haskell中处理异常时,要非常小心何时对事物进行求值,以确保异常在异常处理程序的动态范围内引发,并且不会由于延迟求值而延迟。