Proposal: Add exception info

Michael Sloan mgsloan at gmail.com
Wed Apr 22 03:29:11 UTC 2015


On Tue, Apr 21, 2015 at 6:40 PM, davean <davean at xkcd.com> wrote:

> So, I've had a number of issues with exceptions. This has been one of
> them. I don't really like this proposal as it stands though as it seems to
> make catch a specific exception with said extra info more difficult.
>
> This is data Control.Exception can move around on its own though, right?
> The problem really isn't passing it internal, we could just make a (Stack,
> SomeException) tuple just fine, in theory I think (I'll admit I've not
> actually reviewed the code, and this isn't meant as a complete proposal but
> more a thought experiment). The problem is code handling the data and
> working with old code while not losing any of the power of the current
> system.
>
> So we start with: catch :: Exception e => IO a -> (e -> IO a) -> IO a
>
> Now this proposal allows: catch :: IO a -> (SomeException -> IO a) -> IO a
> If we want access to the new information, but that's not really
> satisfactory.
>
> Real code regularly wants to (picking an arbitrary instance of Exception)
> do: catch :: IO a -> (IOError -> IO a) -> IO a
> only we still want new data.
>

There is no way to always pass around the new data without breaking the
Control.Exception API or having users add extra fields to their data
types.  This is a fundamental issue, and one that my proposal does not seek
to address.  Infact, I acknowledge it at the end - fromException now loses
data.  To me it is quite acceptable because:

* This is a fundamental limitation of the existing Control.Exception API.
This proposal allows us to gracefully update to a new API which does
preserve the new info when catching / rethrowing.

* These extra annotations are primarily for debugging purposes.  It
shouldn't be a correctness issue for them to be lost due to rethrowing
something other than SomeException.

Now one could do something like: catch :: IO a -> (Stack -> IOError -> IO a)
>  -> IO a
> but that is not very upgradable and it breaks existing code.
>
> But this is just a matter of requesting information, so one could do
> something like: catch :: IO a -> (WithStack IOError -> IO a) -> IO a
> where: data WithStack e = WithStack Stack e
> Or maybe one just addes: catchWithContext :: Exception e => IO a ->
> (Context -> e -> IO a) -> IO a
> Or: catchWithContext :: Exception => IO a -> (Context e -> IO a) -> IO a
>
> Now existing code continues to run and we can feed our exception handlers
> the data they want, even when we want some specific exception instead of
> just any exception.
>

This is a good idea, which is directly supported by this proposal.  You
would simply have the implementation of fromException populate the info in
your With* datatype.   Or, the definition I would prefer:


    data WithExceptionInfo e = WithExceptionInfo e [SomeExceptionInfo]
        deriving Typeable

    instance Exception e => Exception (WithExceptionInfo e) where
        fromException (SomeExceptionWithInfo e infos) =
            fmap (\e' -> WithExceptionInfo e' infos) (cast e)
        toException (WithExceptionInfo e infos) =
            SomeExceptionWithInfo e infos

Does this help clarify my proposal?  As far as I can tell there is no
contradiction or difference between our proposals.  I think you would end
up with essentially the same thing I have (maybe with different names ;) ),
if you tried implementing your ideas in the context of Control.Exception.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20150421/49ccd9ef/attachment.html>


More information about the Libraries mailing list