On Mon, Jun 28, 2010 at 10:02 PM, Carl Howells <chowells79@gmail.com> wrote:
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.

I'm not sure if it's related to this, but I had a problem[1] using the ContT instance of MonadCatchIO, but there the result was the opposite: I would have resources released twice. I would really like to have that one solved as well.

Michael

[1] http://permalink.gmane.org/gmane.comp.lang.haskell.cafe/76262