[Haskell-cafe] MonadCatchIO and bracket.

2010-06-28 Thread Carl Howells
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
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] MonadCatchIO and bracket.

2010-06-28 Thread Neil Brown

On 28/06/2010 20:02, Carl Howells 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:
   


See also this recent thread on haskell-cafe: 
http://www.haskell.org/pipermail/haskell-cafe/2010-June/079198.html 
which concluded that the ContT instance in MonadCatchIO is broken.  I 
think as you say, the instances of that library are flawed when dealing 
with monads that can alter the control flow.


Thanks,

Neil.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] MonadCatchIO and bracket.

2010-06-28 Thread Michael Snoyman
On Mon, Jun 28, 2010 at 10:02 PM, Carl Howells chowell...@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
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] MonadCatchIO and bracket.

2010-06-28 Thread Sterling Clover
There’s a history of rich debate and discussion on these issues coming
from the scheme world, where it takes the guise of implementing
unwind-protect in the presence of call/cc. Kent Pitman’s take is
presented here:

http://www.nhplace.com/kent/PFAQ/unwind-protect-vs-continuations-overview.html

There’s some context given in the following ltu discussion:
http://lambda-the-ultimate.org/node/2966

Will Clinger’s notes on his revised implementation are particularly useful:
http://www.ccs.neu.edu/home/will/UWESC/uwesc.sch

Note that none of the implementations translate directly, I think, as
they rely on top-level mutable state.

In any case, my take is the following -- first, remove the ContT
instance from the CatchIO package as it is obviously wrong. Next,
don’t use ContT in the presence of exceptions. There are few cases
where one really needs to do so, given  that exceptions provide a
powerful means of flow control themselves. Finally, it is much easier
to provide a bracket function with one-shot continuations (i.e.,
MonadExit), and doing so could well simplify most of the current uses
of ContT.

Cheers,
Sterl
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe