8
votes

tl;dr

About a week ago I released the 0.1.0.0 package for my first non-trivial Haskell project. I want the executable to be easy to install and upgrade, even for non-Haskellers. In the README, I suggested installing using cabal install. Is this a mistake?

Context

I had heard of "Cabal hell", but didn't realize how darn hard it would be for users to upgrade a globally installed copy of the package, even when I conservatively did not actually change any version dependencies in the .cabal file. In the end, I went down a deep rabbit hole trying to update from 0.1.0.0 to 0.2.0.0. It warned me about breaking dependencies, I tried various incantations to force the upgrade or reset my local state, and wound up borking the system so hard that I had to reinstall the ghc and cabal-install Brew packages (this is on macOS) in order to get everything back to a state in which I could install and run again.

Alternatives:

  • stack install: I was already using Stack to manage local development environment, but it seems to work pretty well for independent installs as well, as long as you have Stack installed first. (Just need to set up your $PATH appropriately.)
  • Distributing a prebuilt binary: Would be nice and easy for end users, but at least on OS X, I'd need to worry about code signing and I don't even have an identity set up for that any more.

So, in my README right now I am mentioning both stack install and cabal install. But what is the 2016 best practice?

1
If users run into "cabal-hell" attempting to install your package it's not really your problem if you have specified your dependencies correctly. For installing an application cabal users can always use a sandbox if they run into package version issues. Both stack and cabal have their place in the Haskell ecosystem. I think the best practice is to make sure your application can be installed using either.ErikR

1 Answers

4
votes

Having looked at your .cabal file I see that you don't have any bounds on your dependencies. You really should have at least lower bounds and preferably both lower and upper bounds.

As @Emanuel Borsboom mentioned, you can have stack fill in the version constraints for you when you upload a package to Hackage with:

stack upload --pvp-bounds=both

In fact, for applications I suggest including the cabal.config file generated by cabal freeze in the package:

cabal freeze
mv cabal.config cabal.config-sample

When running into trouble building legacy applications from Hackage I've often wished the authors had included this information. You can get the cabal.config file for a particular snapshot at:

https://www.stackage.org/{RESOLVER}/cabal.config

And in your stack.yaml file I would use a standard LTS version rather than a nightly-. Supposedly they will never get deleted. On the other hand you'll be helping out your users by reducing the number of snapshot directories they have to maintain.