[Haskell-cafe] Re-exports of resourcet in conduit

Myles C. Maxfield myles.maxfield at gmail.com
Sun Jun 3 01:33:28 CEST 2012

To: Michael Snoyman
CC: haskell-cafe

I'm having a problem working with the conduit library, and was hoping
you could help me out.

Data.Conduit re-exports ResourceT, MonadResource, and MonadThrow (but
not ExceptionT) from Control.Monad.Trans.Resource. I have a conduit
which operates on a monad in the MonadThrow class. I am trying to
figure out which MonadThrow class this should be (the
Data.Conduit.MonadThrow class, or the
Montrol.Monad.Trans.Resource.MonadThrow class, since apparently GHC
doesn't recognize them as the same, even though one is just a
re-export of the other).

If a user of this conduit wants to chain this conduit up with
something like sourceFile, the underlying monad has to be a member of
Data.Conduit.MonadResource and whatever MonadThrow class I chose to
use. I would like to be able to use an existing instance to lift the
class of the inner monad to the class of the entire monad stack (so I
don't have to tell the user of my conduit that they have to define
their own instances), and the only rule that I can find that does that
is the following from Data.Conduit:

Data.Conduit.MonadThrow m => Data.Conduit.MonadThrow (Data.Conduit.ResourceT m)

However, GHC doesn't seem to think that
Control.Monad.Trans.Resource.ExceptionT is an instance of

    No instance for (Data.Conduit.MonadThrow (ExceptionT IO))
      arising from a use of `.....'

Control.Monad.Trans.Resource has a similar instance:

Control.Monad.Trans.Resource.MonadThrow m =>
(Control.Monad.Trans.Resource.ResourceT m)

but because sourceFile operates in the Data.Conduit.MonadResource
class, and Control.Monad.Trans.Resource.ResourceT isn't a member of
that class (it's only a member of
Control.Monad.Trans.Resource.MonadResource), that doesn't help:

    No instance for (Data.Conduit.MonadResource
                       (Control.Monad.Trans.Resource.ResourceT (ExceptionT IO)))
      arising from a use of `.....'

It should be noted that neither module defines anything like the following:

MonadResource m => MonadResource (ExceptionT m)

It seems like the underlying problem here is that:
1) I am required to use the Control.Monad.Trans.Resource.ExceptionT
class, because Data.Conduit doesn't re-export it
2) I am required to use the Data.Conduit.MonadResource class, because
sourceFile and others require it
3) There doesn't seem to be an existing instance that bridges between the two.

This seems like a fundamental flaw with re-exporting; it can only work
if you re-export every single last thing from the original module.
This doesn't seem tenable because the orignal module might not be
under your control, so its author can add new symbols whenever he/she
wants to.

I see two solutions to this problem:
1) Re-export Control.Monad.Trans.Resource.ExceptionT in Data.Conduit.
This will work until someone adds something to the resourcet package
and someone wants to use the new addition and Data.Conduit.ResourceT
in the same stack
2) Don't re-export anything in Data.Conduit; make sourceFile and
others explicitly depend on types in another module, but this might
break compatibility with existing programs if they use fully-qualified
symbol names.
3) Make anyone who wants to use a monad stack in MonadThrow and
MonadResource define their own instances. This is probably no good
because it means that many different modules will implement the same
instance in potentially many different ways.

I feel like option 2) is probably the best solution here. I'm
perfectly happy to issue a pull request for whichever option you think
is best, but I don't know which solution you think is best for your
project. What do you think?


More information about the Haskell-Cafe mailing list