15

I am the maintainer of a package on hackage, lrucache. I recently received a feature request for adding instances for Binary and NFData. Both of those are useful things to have, and I have no issue with those instances, in principle.

However, both of them introduce new package dependencies, and I want to keep my package's dependency list as minimal as possible. Is there a sane way to handle this? There are probably well over twenty different packages that provide useful type classes the data structures in lrucache could implement, and get some benefit from.

Obviously, adding all of them as dependencies is a non-starter. But what else can be done?

I can add flags to lrucache.cabal that will enable compiling various instances. That works, in terms of making the dependency list minimal, except when you want it. But it's horrible in the real world, because you can't specify build flags in build-depends sections. So you can depend on a package with a particular flag, but not specify that dependency. This quickly reduces to near-uselessness.

I can create a bunch of orphan instance packages. This has the advantage of allowing dependencies on those instances to be specified in a build-depends section. Its main disadvantage is adding a ton of extra packages to hackage, and needing to maintain them as separate packages.

What else can I do? What's the right thing to do?

5
  • 2
    The "orphan instances package" approach seems to be a common strategy, at least. The right thing to do is probably invent better dependency management, but... Sep 15, 2011 at 20:32
  • 1
    @C. A. McCann, I personally dislike it when software uses ./configure --enable-Z. I like things simple -- having a package marked installed means it's installed, not "it's installed with options X and Y but not Z". Perhaps Hackage could organize orphan instances packages better, but I don't think they're illegitimate in concept. Sep 15, 2011 at 22:57
  • @gatoatigrado: I agree. Cabal organizing them better would also qualify as "better dependency management" as I mentioned, so feel free to invent it. :] I'm not sure what the obstacles to implementing a system like that would be. Sep 15, 2011 at 23:03
  • Possible duplicate of something like stackoverflow.com/questions/7043442/… ?
    – ivanm
    Sep 16, 2011 at 1:11
  • 1
    nfdata can have the instance in the main package -- its a dependency for plenty of core packages already. binary used to be the only go-to serialization package, but there are a few competitors now. For that, a separate package could be okay, but if the serialization code is straightforward enough, you could just provide it in docs/a wiki page, and let users write their own instance or serialization code when necessary.
    – sclv
    Sep 16, 2011 at 13:12

4 Answers 4

7

Short of an optional dependencies system in cabal (the packaging system) itself, the options aren't too good.

One option is as follows. You can create an additional package of your own for each additional package that you may want your main package to integrate with.

For example, if you would like lrucache to integrate with binary, you could make an additional package lrucache-binary containing the integration (in this case, type class instances). Likewise, a new package of your own lrucache-nfdata to integrate lrucache with nfdata.

Then if someone would like both lrucache and its integration with binary, he can depend on [binary, lrucache, lrucache-binary] together.

1
  • That's exactly what the "orphan instance packages" thing I mentioned is, I think.
    – Carl
    Sep 16, 2011 at 0:40
3

What if you just maintain two packages: lrucache, which depends on a zillion different things, and then lrucache-lite (or lrucache-minimal) which is more or less what you have now. Then if people want to minimize their dependencies, they use the lite version, and if they don't care about having a zillion dependencies, they use the standard version. The big one would probably depend on the lite one, to avoid code duplication.

1
  • I've seen this with the names lrucache and lrucache-instances. That way there are only two packages, but there's no ability to pick and choose instances.
    – John L
    Sep 16, 2011 at 8:01
1

A bit late to the game, but my two cents:

  1. A libraries public API (which includes class instances) should never change based on build flags or package availability - it is punishing for down-stream devs and distro package maintainers.

  2. I'll add a dependency without question to anything in the Haskell Platform (except maybe 'unix' or 'win32' or the like).

That still leaves 'binary', however according to its Hackage page it is available in multiple Linux distributions, and in my experience installs cleanly on Windows. In this case I would accept a patch, but not author it myself.

This doesn't really answer the question straight out, but hopefully illustrates the thought process I would use.

0

I can add flags to lrucache.cabal that will enable compiling various instances. That works, in terms of making the dependency list minimal, except when you want it. But it's horrible in the real world, because you can't specify build flags in build-depends sections. So you can depend on a package with a particular flag, but not specify that dependency. This quickly reduces to near-uselessness.

This might be exactly what you say doesn't work for you above, but can you include your imports and class definitions in conditionally-compiled CPP pragmas? I suppose wrapped around imports of internal modules which in turn import one of your "optional dependencies".

I haven't tried this, but am very interested in knowing the answer as well.

1
  • Yes, that works. But lets say that I did it, for instance, for a Binary instance. Let's say you're writing a program that uses lrucache, and wants to use the Binary instance to serialize it. There would be no way to indicate that in the build-depends section of your cabal file. That's the problem with that approach.
    – Carl
    Sep 16, 2011 at 3:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.