Thoughts on CallStack and withFrozenCallStack

Eric Seidel eric at seidel.io
Thu Sep 8 01:14:16 UTC 2016



On Wed, Sep 7, 2016, at 15:10, Edward Z. Yang wrote:
> I've been adding CallStack to Cabal's source code, and I noticed
> a common pattern that doesn't seem to be supported by the GHC.Stack
> API.
> 
> Imagine I'm writing a general purpose logging function:
> 
>     debug :: Verbosity -> String -> IO ()
> 
> I'd like this function to print out the SrcLoc of the person who
> called; as an additional requirement, I only want to print the
> top-most stack frame by default (someone can ask for the full
> stack if they really want to).
> 
> If I look at callStack, it is going to contain a stack frame for debug
> itself. That's not so great; the obvious way to fix this is to pop off
> the irrelevant frame manually.  

Inside debug, the top element of the callStack should be the call-site
of debug, which should be exactly what you want. (NB this is new
behavior as of GHC 8.0.1, in GHC 7.10.2 you would get an extra frame for
the occurrence of ?callStack)

> But now suppose that I have a pprDebug
> which itself calls debug.  I don't want pprDebug to show up in the stack
> frame!
> 
> Declaratively, I want to say, "Please don't add me to the call stack",
> at the function *definition*, not the call-site (which is what
> withFrozenCallStack) gives me.  Does this seem like a reasonable request?

Indeed, this sounds useful. The approach I recently took with adding
CallStacks to logs was to have the internal functions (eg formatting and
actually printing the logs) take an *explicit* CallStack, and have the
external functions take an *implicit* CallStack. Thus the internal
functions are excluded from the stack. But this does give us slightly
less reuse as, in your example, you couldn't implement pprDebug in terms
of debug if you also want to export debug.

> I have several other ways to do this:
> 
>     - You could just use withFrozenCallStack at the definition site.
>       Then when pprDebug calls debug, debug will get the call stack that
>       pprDebug had; thus, there will always only be one irrelevant frame
>       to pop off.
> 
>     - One problem with using withFrozenCallStack is that you *do* lose
>       useful trace information, which you kind of might actually want
>       if you are being verbose.  So it almost seems like, the frozen
>       API should still *push* frames, but just not make them visible
>       by default.

Good point! Another way this could be useful is if you happen to have a
frozen CallStack already in-scope at the call-site of debug/pprDebug. In
that scenario, the call-site of the logging function would be lost, and
you'd end up printing whatever location was at the top of the frozen
stack, which would be quite confusing!

Thanks for the comments!
Eric


More information about the ghc-devs mailing list