We recently had a very large discussion of stack on Reddit, which I thought was great for kicking off some discussion. In that discussion, there was a very active thread about how stack relates to the Package Versioning Policy (aka, the PVP).

The PVP - and in particular its policy on preemptive upper bounds - has been discussed at great length many times in the past, and I have no wish to revisit that discussion here. I also went into some detail on Reddit explaining how stack and Stackage relate to the PVP, and won't repeat those details here (tl;dr: they're orthogonal issues, and upper bounds are neither required nor discouraged by either).

However, the discussion brought up one of my long-held beliefs: "the right way to solve this is with tooling." Specifically: manually keeping track of lots of lower and upper bounds is a hard, tedious process that many people get wrong regularly. One great effort in this direction is the Hackage Matrix Builder project, which helps find overly lax (and, I believe, overly restrictive) bounds on Hackage and report them to authors. I'm announcing an experimental feature I've just added to stack master, and hoping it can help with the initial creation of upper bounds.

The feature itself is quite simple. When you run the sdist or upload commands, there's a new option: --pvp-bounds, which can be set to none (don't modify the cabal file at all), upper (add upper bounds), lower (add lower bounds), and both (add both upper and lower bounds). The default is none (we shouldn't change an author's cabal file without permission), but that default can be overridden in the stack.yaml file (either for your project, or in ~/.stack/stack.yaml). The algorithm is this:

That was a bit abstract, so let's give an example. Support you're using LTS 3.0, which includes aeson-0.8.0.2, attoparsec-0.12.1.6, and text-1.2.1.3. Let's further say that in your cabal file you have the following:

build-depends: aeson >= 0.7
             , text < 2
             , attoparsec

If you specify --pvp-bounds both on the command line, you'll end up with the following changes:

Just to round out the feature description:

The motivation behind this approach is simplicity. For users of stack, your versioning work usually comes down to choosing just one number: the LTS Haskell version (or Stackage Nightly date), which is relatively easy to deal with. Managing version ranges of every single dependency is an arduous process, and hard to get right. (How often have you accidentally started relying on a new feature in a minor version bump of a dependency, but forgotten to bump the lower bound?) Now, stack will handle that drudgery for you.

Of course, there are cases where you'll want to tell stack that you know better than it, e.g. "I'm using a subset of the aeson API that's compatible between both 0.7 and 0.8, so I want to override stack's guess at a lower bound." Or, "even though the PVP says text-1.3 may have a breaking change, I've spoken with the author, and I know that the parts I'm using won't be changed until version 2."

This feature should still be considered experimental, but I'm hopeful that it will be an experiment that works, and can make both upper bounds advocates and detractors happy. As a member of the latter group, I'm actually planning on trying out this functionality on some of my packages for future releases.

There are still downsides to this feature, which are worth mentioning:

Do you like this blog post and need help with DevOps, Rust or functional programming? Contact us.

Share this