【问题标题】:Failing to catch exception thrown by "pure" function未能捕获“纯”函数引发的异常
【发布时间】:2019-03-26 01:00:56
【问题描述】:

我正在尝试捕获由“纯”代码引发的自定义异常。我不明白我的程序的行为。

我正在使用 GHC 8.6.3。这是我的代码:

import Control.Exception

newtype Problem = Problem String deriving Show

instance Exception Problem

foo :: Int -> Int
foo n = n + (throw $ Problem "Whoops")

baz :: IO Int
baz =
  return (foo 1)
  `catch` \(Problem _) -> return 100

main = do
  n <- baz
  print n
  `catch` \(Problem msg) -> putStrLn msg

我希望异常被第一个处理程序捕获,并让程序打印“100”。相反,它被第二个处理程序捕获并打印“哎呀”。

为什么异常在main 而不是baz 中被捕获?如何在baz 中捕获异常?

【问题讨论】:

    标签: haskell exception


    【解决方案1】:

    由于懒惰,foo 1 在您实际尝试打印 n 的值之前不会被执行。本质上,n 绑定到未评估的 thunk foo 1,而不是 foo 1 的结果。

    强制baz 处理异常的一种相当笨拙的方法是使用seq;几乎可以肯定有一个更优雅的解决方案。

    baz = let result = foo 1
          in seq result (return result) `catch` \(Problem msg) -> return 100
    

    感谢@Alec,更优雅的解决方案是在原始函数中将return 替换为Control.Exception.evaluate

    baz :: IO Int
    baz =
      evaluate (foo 1)
      `catch` \(Problem _) -> return 100
    

    【讨论】:

    • seq result (return result) 有一个方便(稍微更高效)的函数:evaluate。另外,不需要 let 绑定! evaluate (foo 1) `catch` \(Problem msg) -&gt; putStrLn msg
    • s/almost//。 :)
    • 糟糕!我的意思是:baz = evaluate (foo 1) `catch` \(Problem msg) -&gt; return 100。我被你的catch 身体甩了(它错了!)。
    • @Alec, evaluate 效率并不高;它表现得更好。
    • @Alec 啊,是的,这就是导致我在尝试测试evaluate 时遇到问题的原因。我的测试文件是正确的,但后来我在答案中盲目地复制了 baz 的错误定义。现在一切都解决了。
    猜你喜欢
    • 1970-01-01
    • 2017-10-03
    • 2012-06-05
    • 2019-11-02
    • 2020-08-24
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多