Commit c1050953 authored by Alp Mestanogullari's avatar Alp Mestanogullari 🦑 Committed by Ben Gamari
Browse files

tentative improvement to callstack docs

This is an attempt at clarifying the docs for HasCallStack in both the
user guide and libraries/base/GHC/Stack/Types.hs. The example used right
now is built around an hypothetical 'error' function that doesn't itself
print call stacks, and the fact that this doesn't hold makes it all
confusing, see #14635.

Reviewers: hvr, bgamari

Reviewed By: bgamari

Subscribers: rwbarton, thomie, carter

GHC Trac Issues: #14635

Differential Revision: https://phabricator.haskell.org/D4317
parent 906983ca
......@@ -14895,28 +14895,67 @@ 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.
For example, we can define ::
A function can request its call-site with the ``HasCallStack`` constraint
and access it as a Haskell value by using ``callStack``.
 
errorWithCallStack :: HasCallStack => String -> a
One can then use functions from ``GHC.Stack`` to inspect or pretty
print (as is done in ``f`` below) the call stack.
 
as a variant of ``error`` that will get its call-site (as of GHC 8.0,
``error`` already gets its call-site, but let's assume for the sake of
demonstration that it does not). We can access the call-stack inside
``errorWithCallStack`` with ``GHC.Stack.callStack``. ::
f :: HasCallStack => IO ()
f = putStrLn (prettyCallStack callStack)
 
errorWithCallStack :: HasCallStack => String -> a
errorWithCallStack msg = error (msg ++ "\n" ++ prettyCallStack callStack)
g :: HasCallStack => IO ()
g = f
 
Thus, if we call ``errorWithCallStack`` we will get a formatted call-stack
alongside our error message.
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``.
 
.. code-block:: none
 
ghci> errorWithCallStack "die"
*** Exception: die
ghci> f
CallStack (from HasCallStack):
errorWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
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:
.. code-block:: none
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
.. code-block:: none
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 ::
......
......@@ -75,25 +75,28 @@ type HasCallStack = (?callStack :: CallStack)
-- For example, we can define
--
-- @
-- errorWithCallStack :: HasCallStack => String -> a
-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
-- @
--
-- as a variant of @error@ that will get its call-site. We can access the
-- as a variant of @putStrLn@ that will get its call-site and print it,
-- along with the string given as argument. We can access the
-- call-stack inside @putStrLnWithCallStack@ with 'GHC.Stack.callStack'.
--
-- @
-- @
-- errorWithCallStack :: HasCallStack => String -> a
-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
-- putStrLnWithCallStack msg = do
-- putStrLn msg
-- putStrLn (prettyCallStack callStack)
-- @
--
 
-- Thus, if we call @errorWithCallStack@ we will get a formatted call-stack
-- Thus, if we call @putStrLnWithCallStack@ we will get a formatted call-stack
-- alongside our string.
--
--
 
-- >>> errorWithCallStack "die"
-- >>> putStrLnWithCallStack "hello"
-- hello
-- CallStack (from HasCallStack):
-- CallStack (from HasCallStack):
-- putStrLnWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
--
--
-- GHC solves 'HasCallStack' constraints in three steps:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment