The ^>= operator in .cabal files

Don’t worry, it was mostly a sort of joke. :slight_smile:

1 Like

So what happens when somelib-3.5 is released and it’s compatible with the dependent package? If the quoted definition is exact, Cabal is supposed to reject it by default. Should the package author then update the bounds to somelib ^>= 3.4.2 || somelib ^>= 3.5? And for the next release somelib-3.6 to somelib ^>= 3.4.2 || somelib ^>= 3.5 || somelib ^>= 3.6 etc? This doesn’t seem practical.

The soft bounds generated by somelib ^>= 3.4.2 should be read as somelib >= 3.4.2 && somelib <= the-latest-published-version. I hope that’s the actual semantics of allow-newer: ^somelib.

1 Like

I think the best plan is to use the {} set notation for this case. It’s still pretty tough for when you actually support a very wide range of versions. In general packages should probably only be listing the versions that they actually test against, which is likely not that many.

After @david-christiansen posted this I tried out switching one of my packages to the ^>={} notation as an experiment. One thing that didn’t quite work out is that I support text all the way back to 0.2 because the only things I use from it are pack from the strict and lazy modules. It’s probably useless to claim to support that many versions, but it starts to explore the problem you’re asking about.

3 Likes

Yes, when the package author learns that the changes in the major version number didn’t break compatibility then the bounds should be augmented. The whole point of ^>= is that you’re using the package in a way that minor version changes (according to the PVP) wouldn’t break your package. But major version numbers can change anything, so you’ll need to revisit the issue and then declare that things are still OK.

FWIW One thing I’ve stated doing with my packages is removing all lower bounds, test with cabal’s --prefer-oldest flag in CI, and add back lower bounds as necessary.

Technically, all I’m doing in CI is testing all the lower bounds on the lowest version of GHC I support and all the upper bounds on every version of GHC I support, so I’m not testing the full matrix (oldest version of X dependency + latest version of GHC, etc.). But I think this is good enough coverage. And packages that actually highly depend on a dependency (e.g. persistent for my persistent-mtl library) I also have the specific versions of that dependency I support to the matrix.

2 Likes

That’s a great approach, and ought to be the standard! You could probably easily integrate cabal-plan-bounds into this setup to compare the bounds against what you actually test against, to then either prune the bounds, or add new jobs as needed to fill the holes. And also to check that the upper bound is actually reached. I can assist, as I am interested in more early adopter feedback.

1 Like

I think your example demonstrates a need to be able to declare that you’re using a package in a way that makes even its major updates likely compatible. You should be able to say something like text ^^>= 0.2 meaning I can work with all existing text versions after 0.2, and if in 50 years version 99.0 turns out to be incompatible I’ll add a hard upper bound.

That’s essentially >= 0.2.

3 Likes

It is? Good, I’ll stop feeling guilty and start using it more often.

2 Likes

Yes guessing upper bound is pointless. I am sure I wasted more time sorting out fake upperbound of abandoned packaged that the other way round (but maybe I wasn’t just aware of it).

3 Likes
1 Like

Please don’t. The policy for requiring upper bounds has very good reason, based on the curation experience of many package maintainers and hackage trustees over the years. Its essentially the core point of the PVP. When a package fails due to a dependency changing, this can be confusing to diagnose and disruptive to the ecosystem, especially non-expert users who would not have a clue this is what is going on. However, when a package fails due to a solver failure on an upper bound, the message states clearly (well, relatively clearly, and its ongoing work to improve this) that this is the cause, and further, the remedy – test with a relaxed bound and if successful, request a revision bump – is straightforward to implement.

Whenever a new ghc (with a new base) comes out, there’s a cavalcade of failures across hackage, and the whole community springs into action to resolve these. Hard-won experience shows that this is a much less painful process when upper-bounds are already in place.

It does require sometimes maintainers have to bump bounds they otherwise would not have to. However, the alternative – that all hackage users potentially have to realize that a failure is even bounds related at all, and then have to bisect back to which bound would make sense, if any – is much worse.

6 Likes

My PR makes this a recommendation rather than a requirement. I find that more reasonable spec-wise.

2 Likes

I don’t think that makes a difference. PVP compliance itself is not required, but instead specified on hackage with the RFC-meaning of should. Since we only have recommended/encouraged adherence to the PVP as a whole, there’s no reason to weaken the PVP directly as well.

1 Like

The spec indicates very clearly that this may change:

At some point in the future, Hackage may refuse to accept packages that do not follow this convention. The aim is that before this happens, we will put in place tool support that makes it easier to follow the convention and less painful when dependencies are updated.

1 Like

No worries, as

so it won’t happen anytime soon :wink:

1 Like

Yeah… and I think all this speak is not very spec-like and belongs into faq or some other soft document. Not in the spec.

1 Like

After seeing and helping with the fallout from the (entirely correct and PVP-compliant) release of aeson-2.2.0.0, I’ll add another vote for “please don’t”. Cleaning up the mess involved (for several packages) downloading every release they ever made, grepping through for uses of import Data.Aeson.Parser (which moved to attoparsec-aeson; thank god there was an easy thing to test for), and then revising many Hackage releases so the old versions don’t get pulled into strange build plans.

3 Likes

I think there are some misunderstandings:

  1. hackage doesn’t enforce PVP at the moment anyway: it’s questionable whether the proposed change would have any real world impact, except for tooling that uses the PVP spec. It’s still recommending to use upper bounds.
  2. Actually, I find it highly questionable that the spec talks about ecosystem issues at all… and not only that, it also talks about a specific ecosystem. That makes it a poor spec. It’s a version spec and shouldn’t talk about how to handle your dependencies at all other than giving you expressive syntax to describe them
  3. hackage or any other ecosystem can easily enforce a superset of the spec. That’s entirely reasonable.
  4. Why would I use upper bounds on bytestring if all I’m importing is pack? What is the reason to have the maintainer play whack the mole on upper bounds in this case?

This is the first I have heard of such a thing!! Starting in Haskell in 2010, the upper bound requirement always felt to me like an idea based on theory rather than practice, and that’s what made all of its shortcomings seen even more regrettable. All I’ve ever known are experience reports with the upper bound requirement, which are universally negative.

If there are painful experience reports of a time before(?) the upper bound requirement, I would love to hear them. It might go a long way towards making it more palatable.

1 Like