>From what I understand, the current best practices are to build your
package dependencies like so:

Parsec    MyMonadT
MyMonadT_Parsec   -- orphan instances go here
ProjectPackage

This does mean splitting up your project into three packages, but
decouples the orphan instance into its own package where it can do the
least damage :)

Lets assume the above are modules rather than packages (same difference, but fewer indirections in the explanation to follow): if ProjectPackage imports MyMonadT_Parsec and is itself meant
to be imported by other modules, then that decoupling breaks down
(unless MyMonadT is a private class, in which case there is only
one provider of instances, who can try to manage the situation).

At the very least it should go into its own module which can be
imported only in the places that need it, similar to
Control.Monad.Instances defining the orphan instance for Monad ((->)
r).

Orphan instances aren't themselves bad, I think. But since current
technology doesn't allow for import/export control, they always
indicate a problem, hence the warning.  When possible, the problem
should be avoided, by making either the class or the type private
(if neccessary by wrapping a common type in a newtype). That doesn't mean that the problem can always be avoided, just that there is something that needs attention. Back to that import hierarchy:

Parsec    MyMonadT
MyMonadT_Parsec   -- orphan instances go here
ProjectPackage

If ProjectPackage is meant to be imported, there are at least two ways to proceed. Version A is to split the dependent modules, so that each of them can be used with or without the import.

Parsec    MyMonadT
MyMonadT_Parsec   -- orphan instances go here
ProjectPackageWith -- imports, and implicitly exports, MyMonadT_Parsec
ProjectPackageWithout -- no import, no implicit export

So clients can still use ProjectPackageWithout if they get the
instances by another route. This only works for convenience instances where the instances are nice to provide for clients,
but not needed in ProjectPackage itself - in essence:

ProjectPackageWith(module ProjectPackageWithout) where import MyMonadT_Parsec import ProjectPackageWithout If ProjectPackage actually depends on the existence of those orphan instances, plan B is to delay instance resolution, from library to clients, so instead of importing the orphan instances

module ProjectPackage where import MyMonadT_Parsec
f .. =  .. orphan instances are available, use them ..

(which leads to the dreaded implicit export), you'd just assert their existence:

module ProjectPackage where f :: .. Orphan x => .. ; f .. = .. use orphan instances ..

so the client module would have to import both ProjectPackage and MyMonadT_Parsec if it wants to call 'f', or find another way to provide those instance. Of course, the same concerns apply to the client modules, so you end up delaying all instance resolution until the last possible moment (at which point all the orphans need to be imported).

If there is a main module somewhere (something that isn't itself
imported), that is the place where importing the orphan instances
won't cause any trouble (other than that all the users of such
instances better have compatible ideas about what kind of instance
they want, because they all get the same ones).

If there is no main module (you're writing a library meant to
be imported), you better delay the import of any orphans or
provide both libraryWith and libraryWithout. It isn't pretty.

Claus


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

Reply via email to