Exceptions

Simon Marlow simonmar at microsoft.com
Mon Apr 10 06:40:37 EDT 2006


[oops, sent by accident...]

On 08 April 2006 10:18, John Meacham wrote:

> On Sat, Apr 08, 2006 at 12:11:51AM -0400, David Menendez wrote:
>>> newtype ArithException a = ArithException a
>>> 
>>> data DivideByZero
>>> 
>>> throw (ArithException DivideByZero)
>>> 
>>> your code:
>>> 
>>> data MultiplyByZero
>>> throw (ArithException MultiplyByZero)
>> 
>> How would you use this to write a handler that captures any
>> ArithException?
> 
> You would use the Typeable1 class in Data.Typeable. not perfect, but
> exception hierarchies shouldn't be too deep so it is not so bad.

I mentioned this solution to John G. off-list; the problem is that you
end up needing to parameterise all the classes of exceptions (we'd have
IOException a, ArrayException a, AsyncException a, etc.), and that seems
ugly.

Given that we've already made the Exception type sideways extensible
with a simple application of type classes and dynamic types, we can take
the technique a bit further to create an extensible hierarchy of
exceptions, which is what John's arguing for.

To create a new subclass of exceptions:

---------
class (Typeable x, Show x) => ArithException x

newtype AnyArithException = AnyArithException Dynamic
  deriving Typeable

instance Exception AnyArithException
instance Show AnyArithException where
  show (AnyArithException x) = show x

throwArith :: ArithException x => x -> a
throwArith x = throw (AnyArithException (toDyn x))

matchArith :: ArithException x => AnyArithException -> Maybe x
matchArith (AnyArithException x) = fromDynamic x
---------

and populating the subclass is just as easy as populating the root:

---------
data DivideByZero = DivideByZero deriving (Typeable, Show)
data Overflow     = Overflow     deriving (Typeable, Show)

instance ArithException DivideByZero
instance ArithException Overflow
---------

catching is easy too.  To catch *any* ArithException:

---------
     x `catch` \(e :: AnyArithException) -> print e
---------

To catch a particular ArithException:

---------
     x `catch` 
         \e -> case () of
                 _ | Just DivideByZero <- matchArith e -> print e
                   | otherwise -> throw e
---------

To catch a mixture of ArithException and other exceptions:

---------
  x `catchAny` 
    \e -> case () of
 		_ | Just (IOException x) <- matchException e -> print x
		  | Just arith <- matchException e, 
		    Just DivideByZero <- matchArith arith -> print arith
		  | otherwise -> throwAny e
---------

I like this, it seems like a natural extension of what we've already
proposed.  The fact that you have to use 'throwArith' rather than plain
'throw' is slightly annoying, though.

Cheers,
	Simon


More information about the Haskell-prime mailing list