Consider the following code:
import Control.Exception
import Data.Typeable
handler :: SomeException -> IO ()
handler (SomeException e) = do
case e of
-- COMMENTED OUT DivideByZero -> putStrLn $ "handler: DivideByZero!"
_ -> putStrLn $ "handler: exception is: " (show e) " of type: " (show $ typeOf e)
blowUp = do
putStrLn (show (1 `div` 0))
main = catch blowUp handler
It works just fine and prints out:
handler: exception is: divide by zero of type: ArithException
However if I uncomment the DivideByZero
line, I get the following compile-time error:
testexc2.hs:7:5: error:
• Couldn't match expected type ‘e’
with actual type ‘ArithException’
‘e’ is a rigid type variable bound by
a pattern with constructor:
SomeException :: forall e. Exception e => e -> SomeException,
in an equation for ‘handler’
at testexc2.hs:5:10-24
• In the pattern: DivideByZero
In a case alternative:
DivideByZero -> putStrLn $ "handler: DivideByZero!"
In a stmt of a 'do' block:
case e of
DivideByZero -> putStrLn $ "handler: DivideByZero!"
_ -> putStrLn
$ "handler: exception is: "
(show e) " of type: " (show $ typeOf e)
• Relevant bindings include e :: e (bound at testexc2.hs:5:24)
|
7 | DivideByZero -> putStrLn $ "handler: DivideByZero!"
|
Is there a way to pattern match on SomeException
's wrapped exception (other than the wildcard _
pattern)?
If yes: how?
If not: why?
Thanks!
CodePudding user response:
You could use
fromException :: SomeException -> Maybe e
as in (untested)
handler :: SomeException -> IO ()
handler e@(SomeException e') = case fromException e of
Just DivideByZero ->
putStrLn $ "handler: DivideByZero!"
Nothing ->
putStrLn $ "handler: exception is: " (show e') " of type: " (show $ typeOf e')
or even
handler :: SomeException -> IO ()
handler e@(SomeException e')
| Just DivideByZero <- fromException e
= putStrLn $ "handler: DivideByZero!"
| Just _ <- fromException e :: Maybe IOException
= putStrLn $ "handler: IOException!"
...
| otherwise
= putStrLn $ "handler: exception is: " (show e') " of type: " (show $ typeOf e')
It is also possible to play with Typeable
directly, e.g. using cast
or (maybe) eqT
to test if the exception is of a given type.
Even with current GHC, I believe PatternSynonyms
could be exploited to allow a syntax similar to yours:
{-# LANGUAGE ViewPatterns, PatternSynonyms, TypeApplications #-}
import Control.Exception
-- ArithException, only DivideByZero subcase
pattern ExDivisionByZero <- (fromException -> Just DivideByZero)
-- IOException, all subcases
pattern ExIOException <- (fromException -> Just (_ :: IOException))
handler :: SomeException -> IO ()
handler ExDivisionByZero = putStrLn $ "handler: DivideByZero!"
handler ExIOException = putStrLn $ "handler: IOException!"