<div dir="auto">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.<div dir="auto"><br></div><div dir="auto">== Motivating example ==</div><div dir="auto"><br></div><div dir="auto">Suppose we have<br></div><div dir="auto"><div dir="auto"><br></div><div dir="auto">f :: Int -> Int</div><div dir="auto">f x = g (h x)</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">== Proposed change ==</div><div dir="auto"><br></div><div dir="auto">I'd like to add a function (of whatever name)</div><div dir="auto"><br></div><div dir="auto">recordInCallStack</div><div dir="auto">  :: forall (a :: TYPE rep)</div><div dir="auto">     HasCallStack</div><div dir="auto">  => String</div><div dir="auto">  -> (HasCallStack => a)</div><div dir="auto">  -> a</div><div dir="auto"><br></div><div dir="auto">We could then modify the above program thus:</div><div dir="auto"><br></div><div dir="auto"><div dir="auto" style="font-family:sans-serif">f :: Int -> Int</div><div dir="auto" style="font-family:sans-serif">f x = recordInCallStack msg (g (h x))</div><div dir="auto" style="font-family:sans-serif">  where</div><div dir="auto" style="font-family:sans-serif">    msg = "x = " ++ show x</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">Now when g throws an error, we'll see the message f recorded in its proper position in the call stack.</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">== Implementation ==</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">I believe the cleanest approach would be to add a new constructor to CallStack (perhaps called Message):</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">Message :: String -> CallStack -> CallStack</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">Then recordInCallStack can look like</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif"><div dir="auto">recordInCallStack</div><div dir="auto">  :: forall (a :: TYPE rep)</div><div dir="auto">     HasCallStack</div><div dir="auto">  => String</div><div dir="auto">  -> (HasCallStack => a)</div><div dir="auto">  -> a</div><div dir="auto">recordInCallStack s a =</div><div dir="auto">    let ?callStack = Message s ?callStack</div><div dir="auto">    in a</div><div dir="auto"><br></div><div dir="auto">== Questions ==</div><div dir="auto"><br></div><div dir="auto">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?</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">David</div></div></div></div></div>