summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlp Mestanogullari <alp@well-typed.com>2018-01-21 12:07:58 -0500
committerBen Gamari <ben@smart-cactus.org>2018-01-21 12:07:59 -0500
commit5edb18a962cbfee0ff869b1a77ebf2cd79dd8ef5 (patch)
treec28b8eede49f6d4721e7009391c4e72bbb22aa24
parent0074a08ea9dfd1416aa57a9504be73dcdf7a1e2b (diff)
downloadhaskell-5edb18a962cbfee0ff869b1a77ebf2cd79dd8ef5.tar.gz
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
-rw-r--r--docs/users_guide/glasgow_exts.rst67
-rw-r--r--libraries/base/GHC/Stack/Types.hs23
2 files changed, 66 insertions, 24 deletions
diff --git a/docs/users_guide/glasgow_exts.rst b/docs/users_guide/glasgow_exts.rst
index 34efbfd3e2..4125c33b24 100644
--- a/docs/users_guide/glasgow_exts.rst
+++ b/docs/users_guide/glasgow_exts.rst
@@ -14958,28 +14958,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 ::
diff --git a/libraries/base/GHC/Stack/Types.hs b/libraries/base/GHC/Stack/Types.hs
index d9e755239c..b5858f2fa0 100644
--- a/libraries/base/GHC/Stack/Types.hs
+++ b/libraries/base/GHC/Stack/Types.hs
@@ -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
--- call-stack inside @errorWithCallStack@ with 'GHC.Stack.callStack'.
+-- 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
--- errorWithCallStack msg = error (msg ++ "\\n" ++ prettyCallStack callStack)
+-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
+-- putStrLnWithCallStack msg = do
+-- putStrLn msg
+-- putStrLn (prettyCallStack callStack)
-- @
--
--- Thus, if we call @errorWithCallStack@ we will get a formatted call-stack
--- alongside our error message.
+-- Thus, if we call @putStrLnWithCallStack@ we will get a formatted call-stack
+-- alongside our string.
--
--
--- >>> errorWithCallStack "die"
--- *** Exception: die
+-- >>> putStrLnWithCallStack "hello"
+-- hello
-- CallStack (from HasCallStack):
--- errorWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
+-- putStrLnWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
--
--
-- GHC solves 'HasCallStack' constraints in three steps: