Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw an exception with CallStack?

I have Exception type UnknownException, which I'd like to include the CallStack when it's thrown.

module Main where

import Control.Exception (Exception, throw)

newtype UnknownException = UnknownException
  { caller :: String
  } deriving (Show)

instance Exception UnknownException

main :: IO ()
main = willThrow

willThrow :: IO ()
willThrow = throw $ UnknownException "willThrow"

I'd like the above example to print logs like this

example-exe: UnknownException {caller = "willThrow"}
CallStack (from HasCallStack):
  willThrow, called at app/Main.hs:16:13 in main:Main
  main, called at app/Main.hs:13:8 in main:Main

but actually printed:

example-exe: UnknownException {caller = "willThrow"}

Also, is it a good practice to include CallStack in exceptions in Haskell?

like image 567
Leo Zhang Avatar asked Jan 02 '19 00:01

Leo Zhang


1 Answers

Yeah I think it is good practice. If you are compiling with profiling, you can simply use

currentCallStack :: IO [String]

from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.

But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So

main :: IO ()
main = print f

f :: Int
f = g

g :: HasCallStack => Int
g = h

h :: HasCallStack => Int
h = error (show callStack)

Will report the call stack up to g, but will omit f. Sadly,

If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.

That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome. It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.

like image 92
luqui Avatar answered Sep 22 '22 04:09

luqui