Catch SomeException with ExceptT

2019-08-07 19:33发布

问题:

I'm trying to use the ExceptT monad transformer to catch any exception thrown by a function, like so:

import Control.Exception
import Control.Monad.Trans.Except

badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero

main :: IO ()
main = do
    r <- runExceptT badFunction
    case r of Left _ -> putStrLn "caught error"
              Right _ -> putStrLn "nope, didn't catch no error" 

... but the exception happily flies by. What am I doing wrong?

Edit: to clarify, the aim is to catch any exception thrown by a function, regardless of how the exception was thrown. If it makes any difference, the real function call is at the bottom of a fairly deep monad transformer stack. I don't mind missing things like thrown strings (bad programmer!).

回答1:

First, you catch your runtime exception. It can be done by using either monad-control (and lifted-base) or exceptions. Michael Snoyman has a nice article comparing the two: Exceptions and monad transformers

Second, you embed the caught exception in ExceptT.

Here's the complete working code:

import Control.Exception.Lifted
import Control.Monad.Trans.Except

badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero

intercept
  :: ExceptT SomeException IO a
  -> ExceptT SomeException IO a
intercept a = do
  r <- try a
  case r of
    Right x -> return x
    Left e -> throwE e

main :: IO ()
main = do
    r <- runExceptT $ intercept badFunction
    case r of Left _ -> putStrLn "caught error"
              Right _ -> putStrLn "nope, didn't catch no error" 

A more compact (but perhaps somewhat less obvious) definition of intercept is

intercept
  :: ExceptT SomeException IO a
  -> ExceptT SomeException IO a
intercept = handle throwE


回答2:

I believe you want throwE, not throw.

Also, it'll be an ArithException, not a SomeException.