6.19.5. HasCallStack¶
GHC.Stack.HasCallStack
is a lightweight method of obtaining a
partial call-stack at any point in the program.
A function can request its call-site with the HasCallStack
constraint
and access it as a Haskell value by using callStack
.
One can then use functions from GHC.Stack
to inspect or pretty
print (as is done in f
below) the call stack.
f :: HasCallStack => IO () f = putStrLn (prettyCallStack callStack)
g :: HasCallStack => IO () g = f
Evaluating f
directly shows a call stack with a single entry,
while evaluating g
, which also requests its call-site, shows
two entries, one for each computation “annotated” with
HasCallStack
.
ghci> f
CallStack (from HasCallStack):
f, called at <interactive>:19:1 in interactive:Ghci1
ghci> g
CallStack (from HasCallStack):
f, called at <interactive>:17:5 in main:Main
g, called at <interactive>:20:1 in interactive:Ghci2
The error
function from the Prelude supports printing the call stack that
led to the error in addition to the usual error message:
ghci> error "bad"
*** Exception: bad
CallStack (from HasCallStack):
error, called at <interactive>:25:1 in interactive:Ghci5
The call stack here consists of a single entry, pinpointing the source
of the call to error
. However, by annotating several computations
with HasCallStack
, figuring out the exact circumstances and sequences
of calls that lead to a call to error
becomes a lot easier, as demonstrated
with the simple example below.
f :: HasCallStack => IO ()
f = error "bad bad bad"
g :: HasCallStack => IO ()
g = f
h :: HasCallStack => IO ()
h = g
ghci> h
*** Exception: bad bad bad
CallStack (from HasCallStack):
error, called at call-stack.hs:4:5 in main:Main
f, called at call-stack.hs:7:5 in main:Main
g, called at call-stack.hs:10:5 in main:Main
h, called at <interactive>:28:1 in interactive:Ghci1
The CallStack
will only extend as far as the types allow it, for
example
myHead :: HasCallStack => [a] -> a
myHead [] = errorWithCallStack "empty"
myHead (x:xs) = x
bad :: Int
bad = myHead []
ghci> bad
*** Exception: empty
CallStack (from HasCallStack):
errorWithCallStack, called at Bad.hs:8:15 in main:Bad
myHead, called at Bad.hs:12:7 in main:Bad
includes the call-site of errorWithCallStack
in myHead
, and of
myHead
in bad
, but not the call-site of bad
at the GHCi
prompt.
GHC solves HasCallStack
constraints in two steps:
- If there is a
CallStack
in scope – i.e. the enclosing definition has aHasCallStack
constraint – GHC will push the new call-site onto the existingCallStack
. - Otherwise GHC will solve the
HasCallStack
constraint for the singletonCallStack
containing just the current call-site.
Importantly, GHC will never infer a HasCallStack
constraint,
you must request it explicitly.
CallStack
is kept abstract, but GHC provides a function
getCallStack :: CallStack -> [(String, SrcLoc)]
to access the individual call-sites in the stack. The String
is the
name of the function that was called, and the SrcLoc
provides the
package, module, and file name, as well as the line and column numbers.
GHC.Stack
additionally exports a function withFrozenCallStack
that
allows users to freeze the current CallStack
, preventing any future push
operations from having an effect. This can be used by library authors
to prevent CallStack
s from exposing unnecessary implementation
details. Consider the myHead
example above, the errorWithCallStack
line in
the printed stack is not particularly enlightening, so we might choose
to suppress it by freezing the CallStack
that we pass to errorWithCallStack
.
myHead :: HasCallStack => [a] -> a
myHead [] = withFrozenCallStack (errorWithCallStack "empty")
myHead (x:xs) = x
ghci> myHead []
*** Exception: empty
CallStack (from HasCallStack):
myHead, called at Bad.hs:12:7 in main:Bad
NOTE: The intrepid user may notice that HasCallStack
is just an
alias for an implicit parameter ?callStack :: CallStack
. This is an
implementation detail and should not be considered part of the
CallStack
API, we may decide to change the implementation in the
future.
6.19.5.1. Compared with other sources of stack traces¶
HasCallStack
does not interact with the RTS and does not require
compilation with -prof
. On the other hand, as the CallStack
is
built up explicitly via the HasCallStack
constraints, it will
generally not contain as much information as the simulated call-stacks
maintained by the RTS.