为什么无法处理异常

Leo*_*ang 6 haskell

我有以下代码:

handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ return $ [1, 2, 3] !! x

printException :: Int -> SomeException -> IO Int
printException x (SomeException e) = do
  print ("Exception", x, e)
  throw e
Run Code Online (Sandbox Code Playgroud)

当我输入handledIO 8ghci时,我希望看到("Exception", 8, "*** Exception: Prelude.!!: index too large")将被打印,但实际上只打印异常.为什么?

dan*_*iaz 12

原因有点微妙,与Haskell的懒惰有关.

让我们使用具有相同问题的此示例版本:

handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ return undefined
Run Code Online (Sandbox Code Playgroud)

问题是handle只捕获在IO作为参数运行时传递的操作时抛出的异常.该操作成功return undefined完成,因为从未在操作内部进行详细检查.但是当其他操作尝试实际检查返回的值时,例如,尝试将其打印到控制台 - 他们遇到了令人讨厌的惊喜.undefinedIO

这种"懒惰异常抛出结果值IO"问题的一个解决方案是使用evaluate函数Control.Exception而不是return.evaluateIO操作返回之前,请注意减少WHNF的参数,因此它会触发异常:

handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ evaluate $ undefined
Run Code Online (Sandbox Code Playgroud)

它现在有效:

*Main> handledIO 10
("Exception",10,Prelude.undefined
Run Code Online (Sandbox Code Playgroud)

此外,您正在使用throw内部IO动作.在内部IO动作时,throwIO比较好throw.