[Haskell-cafe] Takusen - error handling and DBM monad

Bayley, Alistair Alistair_Bayley at invescoperpetual.co.uk
Thu Feb 1 05:05:29 EST 2007


> From: haskell-cafe-bounces at haskell.org 
> [mailto:haskell-cafe-bounces at haskell.org] On Behalf Of Paul Moore
> 
> catcher :: DBException -> DBM mark Session ()
> catcher x = do
>         liftIO $ putStrLn $ show x
> 
> main = do
>    withSession (connect "..." "..." "...") ( do
>      catchDB (do ...
>        )
>        catcher
>      )
> 
> But this doesn't catch errors in the connect call.
> 
> Wrapping the withSession in catchDB doesn't work, as withSession is in
> the IO monad [1], not the DBM one. But using catch with catcher
> doesn't work, as that expects an error handler in the IO monad, but
> catcher is in the DBM monad.


There's an example in the README file, in which we see:

main = flip catchDB reportRethrow $
  withSession (connect "sqlite_db") (do ...

which basically wraps withSession with catchDB. This does catch errors
in the connect call. The difference between your example and this is in
the handler; your handler has type:

> catcher :: DBException -> DBM mark Session ()

whereas the README example handler is from the library, and has type:

> reportRethrow :: CaughtMonadIO m => DBException -> m ()

CaughtMonadIO isn't something we've bothered to explain in the docs, but
this is simply a way of catching exceptions in a monad that is in the
MonadIO class, which is something the standard libraries don't support.
In Control.Exception, catch and friends are stuck in the IO monad. This
problem has been discussed before on this list:
  http://www.haskell.org/pipermail/haskell/2006-February/017547.html
  http://www.haskell.org/pipermail/haskell/2006-April/017893.html

And may well be fixed for Haskell-Prime?
  http://hackage.haskell.org/cgi-bin/haskell-prime/trac.cgi/ticket/110

So you just need to float your handler out one level (so it wraps
withSession), and make it usable in the regular IO monad :-) That should
be as simple as changing the type sig:

> catcher :: CaughtMonadIO m => DBException -> m ()
> catcher x = liftIO $ putStrLn $ show x

There are a couple of simple handlers in Database.Enumerator already,
which I'd recommend you start with:

> basicDBExceptionReporter :: CaughtMonadIO m => DBException -> m ()
> basicDBExceptionReporter e = liftIO (putStrLn (formatDBException e))

> reportRethrow :: CaughtMonadIO m => DBException -> m ()
> reportRethrow e = basicDBExceptionReporter e >> IE.throwDB e

If you need something fancier then I suggest copying the code in
Database.Enumerator and modifying to suit your needs.


> I'm getting very confused about the relationship between the DBM stuff
> and the IO monad. I'm sure there are good reasons for the DBM monad,
> but at the moment I'm missing them, and all I'm doing is trying random
> things, to little avail.

The idea is to prevent resources used in database code from escaping
into other, non-database, parts of your program. This lets us safely use
the with- idiom to manage resources. For example, although code in the
DBM monad has access to the connection object (somewhat indirectly, but
it is there), it cannot return this object out of the DBM monad. When
withSession closes the connection, we can be sure that further access is
not possible. This is a similar technique to that used by the ST monad,
I think. Oleg explains it here:
  http://www.haskell.org/pipermail/haskell/2006-January/017410.html

Please feel free to mail me directly with questions, too, at this
address or alistair at abayley.org (although my access to that maibox
during working hours is sporadic and infrequent).


Al Falloon wrote:
> what does withSession return if there is a DBException?

Well, whatever the handler returns, same as with any other exception
handler. Note that this must have the same type as whatever withSession
returns, and this constraint is enforced by the type of catch/catchDB:

> catchDB :: CaughtMonadIO m => m a -> (DBException -> m a) -> m a

... which is modelled on Control.Exception.catch (for regular IO monad
exceptions):

> catch :: IO a -> (Exception -> IO a) -> IO a


Alistair
*****************************************************************
Confidentiality Note: The information contained in this message,
and any attachments, may contain confidential and/or privileged
material. It is intended solely for the person(s) or entity to
which it is addressed. Any review, retransmission, dissemination,
or taking of any action in reliance upon this information by
persons or entities other than the intended recipient(s) is
prohibited. If you received this in error, please contact the
sender and delete the material from any computer.
*****************************************************************


More information about the Haskell-Cafe mailing list