[Haskell-cafe] MonadCatchIO and bracket.

Carl Howells chowells79 at gmail.com
Mon Jun 28 15:02:26 EDT 2010

While working this weekend on the Snap web framework, I ran into a
problem.  Snap implements MonadCatchIO, so I thought I could just use
bracket to handle resource acquisition/release in a safe manner.
Imagine my surprise when bracket simply failed to run the release
action sometimes.

I quickly determined the times when it doesn't run are when Snap's
monadic short-circuiting is used.  I dug into the source of bracket
(in the transformers branch, though the mtl branch has the same
behavior in these cases, with slightly different code), and the reason
why quickly became obvious:

-- | Generalized version of 'E.bracket'
bracket :: MonadCatchIO m => m a -> (a -> m b) -> (a -> m c) -> m c
bracket before after thing = block $ do
  a <- before
  r <- unblock (thing a) `onException` after a
  _ <- after a
  return r

When monadic short-circuiting applies, the "_ <- after a" line gets
completely ignored.  In discussions with #haskell on this topic, it
quickly became clear that for any monad transformer that can affect
control flow, the definition of bracket in MonadCatchIO doesn't keep
the guarantee provided by bracket in Control.Exception, which is that
the "after" action will be run exactly once.

Because of that, I think bracket needs to be a class function.
Furthermore, I think it needs to be a new class, ie

class MonadCatchIO m => MonadBracketIO m where
   bracket :: m a -> (a -> m b) -> (a -> m c) -> m c

This would allow its definition in cases where it makes sense (Snap or
MaybeT IO), but it could be left out in cases where it doesn't make
sense, like ListT IO, even though MonadCatchIO makes sense there.

Carl Howells

More information about the Haskell-Cafe mailing list