
#12096: Attach stacktrace information to SomeException -------------------------------------+------------------------------------- Reporter: ndtimofeev | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: Component: Core Libraries | Version: 8.0.1 Resolution: | Keywords: Exceptions Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by ekmett): Replying to [comment:17 ezyang]:
Your wiki page does comment that call stacks are preserved if you rethrow SomeException. But this often doesn't happen in practice. For example, the `system-fileio` example above rethrows an `IOError`: oops, call stack lost.
Yes, there is no correct way to preserve this information that doesn't require the user signalling intent to us. Is what they are throwing a truly fresh exception or does it derive from one they were given? On the other hand, the very callstack was information you lacked to begin with in the old story. If we extend SomeException you can write code that is compatible with both the old and new story, and you can make a couple of one line tweaks to your code to make it so you can preserve the shiny new callstack information. If we extend _every_ exception type to carry this information then there is no code that has ever been written against the exception hierarchy that can survive the change. Moreover, the Exception class itself then has to provide a means for us to get in and find and replace the callStack in these user definable data types, and the user has to construct an empty callstack to throw their exception in the first place, all of which seems like a messy, invasive, and slow design. If we offer some subset of combinators (subject to bikeshedding) like {{{#!hs throwWithCallStack :: Exception e => e -> CallStack -> a withCallStack :: SomeException -> CallStack -> SomeException rethrow :: SomeException -> a rethrowAs :: Exception => SomeException -> e -> a throwIOWithCallStack, rethrowIO, rethrowIOAs ... catchWithCallStack :: Exception e => IO a -> (e -> CallStack -> IO a) -> IO a ... }}} to the user, then they can fix up these cases as they find them, and in the meantime they only get the callstack up to the last `throw`, which is __still__ more information than they have today. The same scenario involving destroying the source location happens in languages like c++ w/ throw vs rethrow. I personally am sad that extending `SomeException` with the callstack would mean my pretty little prisms into `SomeException` for the various exception types become a (convenient) lie, but I don't think the alternative of making the user decorate all of their exception types with a callstack, mangle every throw so that they include an empty callstack to kickstart the exception, and supply a callstack update function, and change all of their existing handlers, most of which do not rethrow, to deal with an extra argument is a terribly practical alternative. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/12096#comment:21 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler