Avoiding orphan instances and dependency blowups

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

Avoiding orphan instances and dependency blowups

Clinton Mead
Hi All

Consider this common situation.

1. Class C in package A.
2. A data type D in package B.
3. There is a clear instance C D but:
4. We do not want A to depend on B as most users of A do not use B.
5. We do not want B to depend on A as most users of B do not use A.

Currently the proposed solutions are:

1. Create a third package, say AB, that depends on both A and B, wrap D in a newtype, and define an instance there.
2. Create a third package, say AB, that depends on both A and B as above, but instead create an orphan instance C D in this package.
3. Just give in and either make A depend on B or B depend on A.

None of these solutions seem very satisfying. The first one, wrapping D in a newtype, destroys much of the interoperability that is attempting to be achieved by defining the instance in the first place. The second solution uses orphan instances and for a variety of reasons many seem to suggest these should be avoided. The third option introduces a snowball of dependencies that the vast majority of the time will be completely unused. 

I've heard some conversations of changes to GHC to deal with this, like "authoritative instances" but nothing seems to have come of it?

I thought to myself, maybe this is a problem that could be solved by the package manager, not GHC.

Here's the draft of how this could work:

1. Create a package, AB, that depends on A and B, but passes an "option" to B, in this case "A". So the dependencies of AB are A and B(A).
2. The package B defines the instance C D, but wrapped in some preprocessor #ifdef style macros. 
3. The package manager passes the requested flags to B during it's compilation, and those flags trigger the sections of code required to be compiled and also additional dependencies. 
4. If a new package is installed adds to the flags of B, B is recompiled. 

This way, packages could define instances for each other but if those dependencies are not used, they do not need to be compiled. But the user has a way to explicitly activate the instances.

My questions:

1. Is this a roughly okay idea?
2. Is there already a better way of doing what I'm proposing (perhaps through backpack which I don't know much about yet)?
3. Is there a way to make this happen currently in Stack or Cabal, perhaps with custom setup scripts, or will this need some significant changes to Stack/Cabal themselves?


Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
Only members subscribed via the mailman list are allowed to post.