为什么HasCallStack在使用withFrozenCallStack时还是会添加栈帧?

Why does HasCallStack still add stack frames when using withFrozenCallStack?

GHC 8 从 GHC.Stack 模块中提供了 HasCallStack,它允许函数在调用时请求记录堆栈帧。它还提供了 withFrozenCallStack 函数,该函数“冻结”调用堆栈,因此无法向其添加更多帧。

在简单的场景中,这符合我的预期。例如:

ghci> let foo :: HasCallStack => CallStack
          foo = callStack
ghci> foo
[("foo",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci2", srcLocFile = "<interactive>", srcLocStartLine = 8, srcLocStartCol = 1, srcLocEndLine = 8, srcLocEndCol = 4})]
ghci> withFrozenCallStack foo
[]

当我正常调用foo时,我得到一个堆栈帧,但是当我用withFrozenCallStack包裹它时,我没有。完美的。然而,当这个例子变得稍微复杂一点时,它就不再像我预期的那样表现了:

ghci> let foo :: CallStack
          foo = bar
          bar :: HasCallStack => CallStack
          bar = callStack
ghci> foo
[("bar",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci9", srcLocFile = "<interactive>", srcLocStartLine = 24, srcLocStartCol = 11, srcLocEndLine = 24, srcLocEndCol = 14})]
ghci> withFrozenCallStack foo
[("bar",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci9", srcLocFile = "<interactive>", srcLocStartLine = 24, srcLocStartCol = 11, srcLocEndLine = 24, srcLocEndCol = 14})]

尽管我使用了 withFrozenCallStack,但通过添加这个简单的间接层,堆栈框架仍然包含在内。为什么?

从概念上讲,我对HasCallStack的理解是,它就像是在当前调用堆栈上隐式使用pushCallStack,而pushCallStack对冻结的调用堆栈没有影响。那么,为什么withFrozenCallStack不阻止将上述堆栈帧添加到调用堆栈中呢?

在您的代码中,foo 是类型 CallStack 的静态值。请注意,它 没有 具有 HasCallStack 约束。

无论您如何以及在何处使用 foo,它始终指的是这个特定的 CallStackfoo 本身使用 bar 定义并不重要,它使用 HasCallStack 机制——您可以静态定义 foo = [("bar",….

尝试将 HasCallStack => 添加到 foo 的类型签名中。它现在的行为是否符合您的预期?