[Haskell-cafe] Newbie Question about Error Handling

Andrew Pimlott andrew at pimlott.net
Sun Jul 24 19:12:45 EDT 2005


On Sun, Jul 24, 2005 at 08:00:58PM +0000, Gerd M wrote:
> Hello!
> I'm confused by the error handling in Haskell. I've written a program that 
> uses a combined monad of type:
> type MyMonad a = ErrorT MyErrorType (StateT MyStateType IO) a
> (MyErrorType is an instance of Error)
> 
> Therefore, to handle IO Errors inside of MyMonad I need to write:
> foo :: MyMonad a
> foo = do ...
>              inp <- liftIO (getChar
>                                `catchError`
>                                (\e -> if isEOFError e then return '\0'
>                                                               else return 
> '?') )
>              case inp of
>                       '\?' -> throwError ...

So you're trying to get the IO error back out into an ErrorT error.  I
made my own exploration of this issue[1] recently and came up with

    liftError :: (MonadError e m, MonadTrans t, MonadError e (t m)) => m a -> t m a
    liftError m = join (lift (liftM return m `catch` (return . throw)))

However, it doesn't help you directly, because it assumes that the error
types are the same in the inner and outer monads.  Also, the definition
is rather compact.  So let's start by just rewriting your code

    foo :: MyMonad a
    foo = do
                minp <- liftIO (do c <- getChar
                                   return (return c)
                                `catchError`
                                (\e -> if isEOFError e
                                         then return (return '\0')
                                         else return (throwError ...))
                                :: IO (MyMonad Char))
                inp <- minp
                ...

We're being clever and returning from IO not a Char, but a MyMonad Char
(mimp).  Then, we run that, which either produces the answer we wanted,
or throws an error in MyMonad.

However, it's still rather cumbersome, so here's a function similar to
liftError that works for your monad.

    -- I'll assume you have such a function
    fromIOError :: IOError -> MyErrorType

    -- The name is intended to convey that IO errors are caught and
    -- reintroduced into MyMonad.  Better suggestions welcome.
    liftIOTrap :: IO a -> MyMonad a
    liftIOTrap io = do mx <- liftIO (do x <- io
                                        return (return x)
                                     `catchError`
                                     (\e -> return (throwError
                                                      (fromIOError e))))
                       mx

    foo :: MyMonad a
    foo = do
                inp <- liftIOTrap (getChar
                                    `catchError`
                                    (\e -> if isEOFError e then return '\0'
                                                           else throwError e))
                ...

Andrew

[1] http://haskell.org/pipermail/haskell-cafe/2005-June/010361.html



More information about the Haskell-Cafe mailing list