Haskell中error函数多次打印Exception的原因及相关疑问
Haskell嵌套调用error函数的异常打印问题
现象展示
单次调用error:
Prelude> error "" *** Exception: CallStack (from HasCallStack): error, called at <interactive>:3:1 in interactive:Ghci1
两次嵌套调用:
Prelude> error (error "") *** Exception: *** Exception: CallStack (from HasCallStack): error, called at <interactive>:2:8 in interactive:Ghci1
三次及更多嵌套调用时,会打印对应数量的*** Exception:提示。
我编写了如下函数,可按输入次数打印异常提示:
printException n = foldr (\c r -> c error r) id (replicate n (.)) ""
同时存在疑问:为何until (const False) error ""不会无限打印Exception?
问题解答
1. 嵌套调用error为何出现多份Exception提示
Haskell采用按需求值策略,调用error (e)时必须先求值参数e,才能完成外层error的调用流程。
以error (error "")为例:
- 外层
error需要获取参数值,因此先触发内层error ""的求值,内层error抛出第一个异常; - 外层
error在尝试获取参数时捕获到该异常,自身也会抛出异常; - GHCi处理未捕获的异常链时,会依次打印每一层触发的异常提示,因此嵌套n层就会出现n份
*** Exception:。
你写的printException n本质是生成n层嵌套的error调用(比如n=3时等价于error (error (error ""))),因此会打印对应次数的异常提示。
2. 为何until (const False) error ""不会无限打印
先看until的标准定义:
until :: (a -> Bool) -> (a -> a) -> a -> a until p f x = if p x then x else until p f (f x)
这里p是const False,永远返回False,但实际只会触发一次异常:
- 第一次执行
f x(即error "")时,会立即抛出异常,直接中断整个求值过程; - 异常抛出后,程序没有机会执行到递归分支
until p f (f x),因此不会触发后续的error调用,自然不会无限打印异常提示。
内容的提问来源于stack exchange,提问作者Damian Rafael Lattenero
相关产品推荐
相关产品推荐

