Reputation: 4832
So my code (by design) gives me an exception, and I need to find out if it belongs to a specific "class" of exceptions — in any sense of the word. I can do it like this:
data Case a = ExpectedException | SuddenException | AsynchronousException | Result a
classifyExceptionM :: forall m. MonadCatch m => m () -> m (Case ())
classifyExceptionM a = fmap Result a `catches`
[ Handler ((\e -> return ExpectedException) :: ArithException -> m (Case ()))
, Handler ((\e -> return SuddenException) :: ArrayException -> m (Case ()))
, Handler ((\e -> return AsynchronousException) :: SomeAsyncException -> m (Case ()))
]
This is ugly but it works. Now I wish to perform the same classification in pure code. Yet again,
I may throw my exception with the pure Catch
monad and catch it again:
classifyException' :: Exception e => e -> Case ()
classifyException' e = either undefined id $ runCatch $ throwM e `catches`
[ Handler ((\e -> return ExpectedException) :: ArithException -> Catch (Case ()))
, Handler ((\e -> return SuddenException) :: ArrayException -> Catch (Case ()))
, Handler ((\e -> return AsynchronousException) :: SomeAsyncException -> Catch (Case ()))
]
Pure, works, but ugly all the same.
What I wish to have is something like this:
classifyException :: Exception e => e -> Case ()
classifyException e = case sortOf e of
DivideByZero -> ExpectedException
IndexOutOfBounds -> SuddenException
StackOverflow -> AsynchronousException
_ -> error "Encountered unclassifiable exception"
— But I could not pattern match on exceptions because of their tragically abstract nature. The closest I can get is this:
classifyException :: SomeException -> Case ()
classifyException (SomeException e) =
if typeOf e == typeOf DivideByZero then ExpectedException else
if typeOf e == typeOf (IndexOutOfBounds "") then SuddenException else
if typeOf e == typeOf (SomeAsyncException StackOverflow) then AsynchronousException else
error $ "Encountered unclassifiable exception: " ++ show e
Notice how I need to specifically select SomeException
as the type of my exception. This is
because all exceptions are secretly wrapped in this type when they are thrown, and typeOf
any
exception I could catch is always SomeException
.
It is all just a sorrowful mess. How can I improve?
Upvotes: 0
Views: 94
Reputation: 1510
I think perhaps your question is more about style than substance, which is why it's not received much attention - the end point is somewhat unclear. Some of your attempts could be tidied, eg.
classifyException' :: Exception e => e -> Case ()
classifyException' e = fromRight (error "blah") $ runCatch $ throwM e `catches`
[ Handler @ArithException $ const $ pure ExpectedException
, Handler @ArrayException $ const $ pure SuddenException
, Handler @SomeAsyncException $ const $ pure AsynchronousException
]
Two other alternatives,
classifyException :: SomeException e => e -> Case ()
classifyException = \case
(fromException -> Just ArithException) -> ExpectedException
(fromException -> Just ArrayException) -> SuddenException
(fromException -> Just AsyncException) -> AsynchronousException
_ -> error "Encountered unclassifiable exception"
or (with some language extension soup)
classifyException e =
if | is @ArithException -> ExpectedException
| is @ArrayException -> SuddenException
| is @AsyncException -> AsynchronousException
| otherwise -> error "Encountered unclassifiable exception"
where is :: forall e. Exception e => Bool
is = isJust $ fromException @e e
Upvotes: 1