我有以下代码:
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.evaluate在IO操作返回之前,请注意减少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.