Proposal: Extensible exceptions
Isaac Dupree
isaacdupree at charter.net
Mon Jul 7 13:51:11 EDT 2008
I have type issues. Look how inconsistent these types are (I think,
copied from the patch); some use forall and some use SomeException:
catchAny :: IO a -> (forall e . Exception e => e -> IO a) -> IO a
setUncaughtExceptionHandler :: (SomeException -> IO ()) -> IO ()
getUncaughtExceptionHandler :: IO (SomeException -> IO ())
Obviously we should have
catchAny :: IO a -> (forall e . Exception e => e -> IO a) -> IO a
setUncaughtExceptionHandler :: (forall e . Exception e => e -> IO ()) ->
IO ()
getUncaughtExceptionHandler :: IO (forall e . Exception e => e -> IO ())
But that requires some kind of impredicative types for
getUncaughtExceptionHandler.
Then, instead, for consistency, obviously we should have
catchAny :: IO a -> (SomeException -> IO a) -> IO a
setUncaughtExceptionHandler :: (SomeException -> IO ()) -> IO ()
getUncaughtExceptionHandler :: IO (SomeException -> IO ())
Then we don't even need the Rank2Types extension?
Also, according to the extensible exceptions paper p. 4 (footnote 3),
`catch` with SomeException type should suffice, such that catchAny is
not needed? Or was it decided that the facility to catch SomeException
should be separated from the facility to catch any more-specific group
of exceptions (since that implementation in the paper looks like a bit
of a hack... or perhaps to warn people that it's a bad idea and should
use a function with a name that's a big flashing warning)?
On a different note: What about strictness? I'll take an arbitrary
example from the paper
data SomeFloatException
= forall a . (Exception a) => SomeFloatException a
deriving Typeable
Then, (SomeException (SomeArithException (undefined ::
SomeFloatException))) is not _|_. I think it's generally a bad idea to
throw exceptions that contain _|_ in their values; it would usually be
just as good to evaluate their values first and if they're _|_, let that
be the exception instead. In this particular case, should the
convention for defining nodes in the exception hierarchy, have a
strictness annotation, such as the following?
data SomeFloatException
= forall a . (Exception a) => SomeFloatException !a
deriving Typeable
data SomeArithException
= forall a . (Exception a) => SomeArithException !a
deriving Typeable
data SomeException
= forall a . (Exception a) => SomeException !a
deriving Typeable
(Since newtype won't work for existentials, we can't use it here.)
This flattens the hierarchy out of the way affecting the semantics,
while still allowing actual exception types to be lazy if they want to
be, e.g.
data DivideByZero = DivideByZero --(a non-lazy error with no variables)
deriving (Typeable, Show)
data ErrorCall = ErrorCall String --not explicitly strict in the string
deriving (Typeable, Show)
This way (error (error ("abc"++error ...))) still works :-P. More
seriously of a reason lack of strictness was annoying was
Debug.Trace.trace interleaving, but that's unsafePerformIO business that
can be changed separately, and has not much to do with exceptions. Or,
error messages that 'show' arguments that weren't already evaluated, and
have errors themselves.. that's happened to me :-)
-Isaac
More information about the Libraries
mailing list