# Roll your own stack traces

(This post seems almost too obvious to write, but I couldn’t find any other instances of people talking about this kind of pattern, or any libraries. Pointers welcome!)

If you’ve written code in Java, Python, or some other language with ubiquitous
exceptions, then you are probably familiar with *stack traces*. Stack traces are
great for a developer because they give you more contextual information about
where in your code an error occurred, and often this can be enough to help you
pin down the bug.

But what about in Haskell?

## Adding context to `MonadError`

Haskell *does* have exceptions, and they *do* now have stack traces. However,
most Haskellers frown on using exceptions for anything other than tricky IO
problems or assertion failures, since they pollute the purity of the code.
Rather, the advice is to return an `Either`

(or, getting a bit fancier, use
`MonadError`

from `mtl`

).

But using this approach gives you no contextual information at all! The error value which you return is created at the site of the error and then short-circuited all the way back up to the top level, with no additional information added.

Here’s a small example, with a little arithmetic expression evaluator that can throw divide-by-zero errors

```
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Except
data Expr = Const Int | Plus Expr Expr | Minus Expr Expr | Div Expr Expr
instance Show Expr where
show (Const i) = show i
show (Plus e1 e2) = "(" ++ (show e1) ++ " + " ++ (show e2) ++ ")"
show (Minus e1 e2) = "(" ++ (show e1) ++ " - " ++ (show e2) ++ ")"
show (Div e1 e2) = "(" ++ (show e1) ++ " / " ++ (show e2) ++ ")"
data Error = DivByZeroError
instance Show Error where
show DivByZeroError = "division by zero"
eval :: (MonadError Error m) => Expr -> m Int
eval e = case e of
Const i -> pure i
e@(Plus e1 e2) -> (+) <$> eval e1 <*> eval e2
e@(Minus e1 e2) -> (-) <$> eval e1 <*> eval e2
e@(Div e1 e2) -> do
e1' <- eval e1
e2' <- eval e2
when (e2' == 0) $ throwError DivByZeroError
pure $ e1' `div` e2'
```

We’re going to feed this an expression with a division by zero, but with two slightly obfuscated divisions in it, so it’s not immediately obvious which one is responsible for the error. This is exactly the sort of situation where a stack trace is useful!

```
printEval :: Expr -> IO ()
printEval = putStrLn . either show show . runExcept . eval
main = printEval $
((Const 1) `Div` ((Const 1) `Minus` (Const 1)))
`Plus`
((Const 2) `Div` ((Const 1) `Plus` (Const 2)))
```

As we expect, we get a rather unhelpful error:

```
division by zero
```

What to do? Well, we’re not devoid of tools. In particular, `catchError`

allows
us to catch an error and rethrow a new one. We can use this to roll our own
contextual error enhancement:

```
data Error = DivByZeroError
| Context String Error
instance Show Error where
show DivByZeroError = "division by zero"
show (Context c e) = c ++ "\n" ++ (show e)
eval :: (MonadError Error m) => Expr -> m Int
eval e = (case e of
Const i -> pure i
e@(Plus e1 e2) -> (+) <$> eval e1 <*> eval e2
e@(Minus e1 e2) -> (-) <$> eval e1 <*> eval e2
e@(Div e1 e2) -> do
e1' <- eval e1
e2' <- eval e2
when (e2' == 0) $ throwError DivByZeroError
pure $ e1' `div` e2')
`catchError` (\err -> throwError $ Context ("evaluating " ++ (show e)) err)
```

Here we’re using `catchError`

to catch any errors thrown by `eval`

, wrap them in
a new error (it has to be of the same type, hence why we need a new `Error`

constructor), and then rethrow them.

Running our program again, we now get something more helpful:

```
evaluating ((1 / (1 - 1)) + (2 / (1 + 2)))
evaluating (1 / (1 - 1))
division by zero
```

So we can see which expression is the problematic one.

Now, it’s not really fair to call this a “stack trace”: it’s more of a “context trace”, and we have to put all the information in ourselves. So it’s less useful for the case where something fails in a way you hadn’t anticipated, but it’s still very useful for cases where you’re expecting errors.

## Generalising a little

We can generalise this a little bit to make what’s going on slightly clearer:

```
data WithContext c e = Plain e | Context c (WithContext c e)
instance (Show c, Show e) => Show (WithContext c e) where
show (Plain e) = show e
show (Context c e) = (show c) ++ "\n" ++ (show e)
withContext :: (MonadError (WithContext c e) m) => c -> m a -> m a
withContext c act = catchError act $ \err -> throwError $ Context c err
eval :: (MonadError (WithContext String Error) m) => Expr -> m Int
eval e = withContext ("evaluating " ++ (show e)) $ case e of
Const i -> pure i
e@(Plus e1 e2) -> (+) <$> eval e1 <*> eval e2
e@(Minus e1 e2) -> (-) <$> eval e1 <*> eval e2
e@(Div e1 e2) -> do
e1' <- eval e1
e2' <- eval e2
when (e2' == 0) $ throwError $ Plain DivByZeroError
pure $ e1' `div` e2'
```

This is slightly nicer at the cost of having to write `WithContext String Error`

in
your constraint, and throw `Plain`

errors when you aren’t adding contexts.

If we actually wanted to make this a library we could improve things even more
by returning a `prettyprinter`

`Doc`

, or maybe offering Template Haskell splices
to throw errors tagged with source locations.