[Haskell-cafe] Catching an escaping exception

Andrew Pimlott andrew at pimlott.net
Fri Nov 4 14:55:18 EST 2005


On Fri, Nov 04, 2005 at 12:15:30PM +0000, Joel Reymont wrote:
> How can I catch this exception and translate it into an error within  
> my monad?
> 
> The exception is going to ghci and is not being caught in the monad.  
> I'm interested in catching it and failing the monad gracefully.
> 
> Exception:
> 
> <interactive>: connect: does not exist (Connection refused)
> 
> Types follow...
> 
> type EngineState = ErrorT String (StateT World IO)
> type EngineResult = IO (Either String (), World)
> 
> connect :: [Prop] -> HostName -> Int -> EngineState ()
> connect env h p =
>     do w <- get
>        let secs = timeout_seconds w
>        (Right h) <- liftIO $ timeout secs $ connect_ h p

The crux of your problem is the implementation of liftIO.  It does not
bring IO error back into EngineState, as you might wish:

    instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where
        liftIO = lift . liftIO

Since lift works for an arbitrary inner Monad (not necessarily a
MonadError), it obviously can't handle errors in the inner monad.
Furthermore, your error type in EngineState is String, but the error
type in IO is IOError, so it's doubly obvious. :-)

Therefore, when you run an EngineState, transforming it into IO, the
IO errors remain uncaught.

If you want to bring errors into EngineState, you need your own lift
function that traps errors (which I wish were provided in
Control.Monad.Error).  I wrote a couple messages about this when I ran
into the same issue:

    http://haskell.org/pipermail/haskell-cafe/2005-July/010892.html
    http://haskell.org/pipermail/haskell-cafe/2005-June/010361.html

Note you'll have to convert the error type from IOError to String, or
change EngineState to use IOError.

Andrew


More information about the Haskell-Cafe mailing list