Proposal: offer a way to augment call stacks

David Feuer david.feuer at gmail.com
Tue Mar 13 23:05:52 UTC 2018


HasCallStack constraints give us a pretty good view of where we came from
when an error occurs, but they don't always tell us enough about what was
going on there at the time. I think we should add a small feature to the
call stack interface to improve matters.

== Motivating example ==

Suppose we have

f :: Int -> Int
f x = g (h x)

If g throws an error because its argument is bad, we'd like to know how
that happened. A HasCallStack constraint on g will reveal that g was called
by f. Now we'd like to dig deeper. Was f supplied a bad argument? Did h
calculate a bad value from it? If f is only called a few times, it's easy
to work this out: just add a trace call to f reporting x. But if f is
called thousands of times, that won't work at all.

== Proposed change ==

I'd like to add a function (of whatever name)

recordInCallStack
  :: forall (a :: TYPE rep)
     HasCallStack
  => String
  -> (HasCallStack => a)
  -> a

We could then modify the above program thus:

f :: Int -> Int
f x = recordInCallStack msg (g (h x))
  where
    msg = "x = " ++ show x

Now when g throws an error, we'll see the message f recorded in its proper
position in the call stack.

== Implementation ==

I believe the cleanest approach would be to add a new constructor to
CallStack (perhaps called Message):

Message :: String -> CallStack -> CallStack

Then recordInCallStack can look like

recordInCallStack
  :: forall (a :: TYPE rep)
     HasCallStack
  => String
  -> (HasCallStack => a)
  -> a
recordInCallStack s a =
    let ?callStack = Message s ?callStack
    in a

== Questions ==

For performance reasons, I believe recordInCallStack should be lazy in its
string argument. But this means that we could encounter another error in
the process of printing a call stack. How if at all should we attempt to
recover in this case?

If we don't do anything special, I believe we may start printing a second
call stack in the middle of the first. Kind of gross.

Another option is to catch all exceptions when evaluating the message. Then
we could note that the message couldn't be printed but continue with the
rest of the call stack.

David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20180313/bf1c59d3/attachment.html>


More information about the Libraries mailing list