gpt4 book ai didi

exception - 如何在 Haskell 中正确使用 Control.Exception.catch?

转载 作者:行者123 更新时间:2023-12-03 11:48:39 25 4
gpt4 key购买 nike

有人可以解释一下以下 ghci 中的行为与行之间的区别:

catch (return $ head []) $ \(e :: SomeException) -> return "good message"

返回
"*** Exception: Prelude.head: empty list


catch (print $ head []) $ \(e :: SomeException) -> print "good message"

返回
"good message"

为什么第一个案例没有捕获异常?为什么它们不同?为什么第一种情况会在异常消息之前加上双引号?

谢谢。

最佳答案

让我们看看第一种情况会发生什么:

catch (return $ head []) $ \(e :: SomeException) -> return "good message"

您创建 thunk head []这是 return编辑为 IO行动。这个 thunk 不会抛出任何异常,因为它没有被评估,所以整个调用 catch (return $ head []) $ ... (类型为 IO String )产生 String thunk 无一异常(exception)。仅当 ghci 之后尝试打印结果时才会发生异常。如果你试过
catch (return $ head []) $ \(e :: SomeException) -> return "good message"
>> return ()

相反,不会打印任何异常。

这也是你得到 _"* Exception: Prelude.head: empty list_ 的原因。GHCi 开始打印以 " 开头的字符串。然后它会尝试评估字符串,这会导致异常,而这被打印出来。

尝试替换 returnevaluate (这迫使它的论点到 WHNF)为
catch (evaluate $ head []) $ \(e :: SomeException) -> return "good message"

然后你将强制 thunk 在 catch 内部进行评估这将引发异常并让处理程序拦截它。

在另一种情况下
catch (print $ head []) $ \(e :: SomeException) -> print "good message"

异常发生在 catch部分当 print试图检查 head []因此它被处理程序捕获。

更新:正如您所建议的,一件好事是强制该值,最好是其完整的正常形式。这样,您可以确保没有“惊喜”在懒惰的重击中等着您。无论如何,这是一件好事,例如,如果您的线程返回一个未评估的 thunk 并且实际上是在另一个毫无戒心的线程中评估的,您可能会遇到难以发现的问题。

模块 Control.Exception已经有 evaluate ,这会强制插入其 WHNF。我们可以轻松地对其进行扩充以使其达到完整的 NF:
import Control.DeepSeq
import Control.Seq
import Control.Exception
import Control.Monad

toNF :: (NFData a) => a -> IO a
toNF = evaluate . withStrategy rdeepseq

使用它,我们可以创建 catch 的严格变体强制对其 NF 执行给定操作:
strictCatch :: (NFData a, Exception e) => IO a -> (e -> IO a) -> IO a
strictCatch = catch . (toNF =<<)

这样,我们可以确定返回的值是完全评估的,所以我们在检查它时不会得到任何异常。您可以验证如果您使用 strictCatch而不是 catch在您的第一个示例中,它按预期工作。

关于exception - 如何在 Haskell 中正确使用 Control.Exception.catch?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18052619/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com