[Haskell-cafe] Re: MonadCatchIO, finally and the error monad
michael at snoyman.com
Mon Oct 18 06:31:27 EDT 2010
On Sun, Oct 17, 2010 at 8:04 PM, Arie Peterson <ariep at xs4all.nl> wrote:
> On Thu, 14 Oct 2010 12:01:59 +0200, Michael Snoyman
> <michael at snoyman.com> wrote:
>> [...] which I believe is a flawed
>> design in the MonadCatchIO-transformers package. Here are my thoughts
>> on this and what I think needs to be done to fix it.
>> Try running the code with each version of go uncommented. In the first
>> two, "sequel called" gets printed. However, in the third, it does not.
>> The reason is short-circuiting: if we remember from the definition of
>> finally, there are two cases we account for. If an exception is
>> called, catch addresses it. If not, we assume that the next line will
>> be called. However, in the presence of short-circuiting monads like
>> ErrorT, that line of code will never get called!
> Yes. That is the behaviour I would expect.
> There are two kinds of exceptional values in, for instance, 'ErrorT e
> IO a':
> • IO exceptions, in the "underlying monad" 'IO';
> • error values of type 'e', in the monad transformer 'ErrorT e'.
> The MonadCatchIO instance for ErrorT deals with the first kind only.
> Catching IO exceptions, and cleaning up after them, is what MonadCatchIO
> was invented for. I feel that I should not decide for all users how
> these two layers of exceptions should interact; keeping the MonadCatchIO
> instance oblivious to the underlying monad as much as possible seems
> like the safest/most general thing to do.
> Meanwhile, I can see why you would want 'finally' to also catch the
> ErrorT errors, in your example, and circumvent the short-circuiting.
> However, I'm not convinced that this is always the right (expected, most
> useful, ...) behaviour. Maybe I just need more convincing :-).
I can't think of a single use case where the current behavior is
desired. On the other hand, many common cases would require the
semantics I'm looking for:
* My use case: returning a resource to a resource pool upon completion
of an action.
* Freeing allocated memory (probably better to use bracket there, but
the same issue exists for that function).
* Closing a file handle at the end of an action (again, a good use
case for bracket).
While we're on the topic, I also found a problem with the ContT
instance of MonadCatchIO which resulted in double-freeing of
memory. I understand from Oleg's comments earlier in this thread
that this is an expected behavior of the ContT monad, but I *don't*
think it's an expected behavior of the bracket_ function. If there's
no way to define a MonadCatchIO instance of ContT that only calls the
cleanup code once, perhaps it doesn't make sense to define that
instance at all.
> By the way: my apologies for not being more responsive and proactive in
> this matter. At the moment, I have very little time for my haskell
> endeavours. But more importantly, my maintainership of
> MonadCatchIO-transformers is mostly coincidental. (I found the
> MonadCatchIO-mtl code very useful, except I needed it for 'transformers'
> instead of 'mtl'. So, I forked it (at that time, only the cabal file
> needed editing), and put it on hackage.)
I hadn't realized that you weren't the maintainer of MonadCatchIO-mtl
(I simply never looked). I've CCed him on this issue as well.
> If you feel that as maintainer I'm more of a roadblock than helping you
> make effective use of this library, then perhaps you should take over
Thank you for the offer, but I don't think I'm in a position to take
over maintainership of another library. However, I think that my
original suggestion of moving all of the exception-handling functions
into the type class itself would solve the current issue; is there a
reason not to do so? I'm still not sure what to do about ContT.
More information about the Haskell-Cafe