AFAIK, there are two ways to get a call stack for debugging in Haskell:
HasCallStack
constraint in the codeghc -prof -fprof-auto-top
My test code:
import GHC.Stack
-- | a wrapper function to make "last" from base traceable
last' :: HasCallStack => [a] -> a
last' xs = case xs of [] -> error "abuse last"; _ -> last xs
-- | a untraceable partial function
foo :: [Int] -> Int
foo xs = last' xs + 1
-- | a untraceable partial function
-- , which looks like traceable, but it's call stack is cut off by "foo"
bar :: HasCallStack => [Int] -> Int
bar xs = foo xs
-- | an empty list
-- , which is also an improper input of "last'"
xs :: [Int]
xs = []
-- | the program is expected to print a call stack telling how "bar" is called
-- , but we can only know that "foo" called "last'" improperly from the printed call stack
main :: IO ()
main = print $ bar xs
Following is the call stack I get from both of the above two ways with this test code:
$ ghc -prof -fprof-auto call-stack-cut-off.hs
$ ./call-stack-cut-off
call-stack-cut-off: abuse last
CallStack (from HasCallStack):
error, called at call-stack-cut-off.hs:5:29 in main:Main
last', called at call-stack-cut-off.hs:9:10 in main:Main
CallStack (from -prof):
Main.last' (call-stack-cut-off.hs:5:1-60)
Main.foo (call-stack-cut-off.hs:9:1-21)
Main.bar (call-stack-cut-off.hs:14:1-15)
Main.main (call-stack-cut-off.hs:24:1-21)
Main.CAF (<entire-module>)
IMO, the call stack from -prof
is already good enough and is easier to use. So I'm wondering why the HasCallStack
mechanism is added yet. Is there some difference between these two way which will significantly affect the debugging experience?
HasCallStack
has a few basic advantages:
It’s lighter-weight, not needing -prof
, so it doesn’t require recompilation (because profiled code has a different ABI than non-profiled code)
It gives you more control over what’s included in the call stack, based on where you place HasCallStack
constraints and where you use withFrozenCallStack
to prevent irrelevant/internal details from showing up in traces
It allows you to access the call stack from within the program itself using getCallStack
, so you can incorporate it into messages for logging, exceptions, and so on
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With