The ^>= operator in .cabal files

Their semantics are definitely different. The whole reason tools can treat them differently is because their semantics are different.

But the fact that we’re still having this debate on a topic that was ideally about clearing up their purpose is not a good sign of how easy they are to work with.

2 Likes

My point is that their semantic are actually the same (because >=2.5 can’t actually mean what it is supposed to mean), thus the ongoing debate and confusion (we are trying to explain the difference between the same thing).

2 Likes

Oh, I think I misunderstand what you’re talking about.

^>= is a lower bound, identical to >=.

But it is also an upper bound. A speculative one. Or “conservative”, if you prefer. ^>= 3.2 means “I don’t know if this works with 3.3 or not.”

It’s the upper bound nature of ^>= that I’ve been talking about. It’s different than < 3.3, which used to mean “I know this doesn’t work with 3.3 OR I have no idea if this works with 3.3”, and now… well… now it could be restricted to mean “I definitely know this doesn’t work with 3.3”. Assuming everyone adopts the caret bound.

I think what @maxigit is trying to say is that instead of writing ^>= 3.2 you could just as well write >= 3.2 (without < 3.3 upper bound) because it is always the case that there is an implicit “I have no idea if this works with 3.3”. It does not make sense to interpret a lower bound (without an accompanying upper bound) in any other way (you can’t know what happens in the future).

One problem with this is that you might know it works with 3.2 and 3.3 but 3.4 does not exist yet. What should you write then? Maybe >= 3.2 && >= 3.3, but that seems redundant.

5 Likes

Oh I see - this does make sense to me. Ha.

This is exactly my point indeed. :slight_smile:

1 Like

It feels to me that this is moving in the direction of having a list of known working versions, plus at most one non-working upper bound. With that in mind, perhaps we should stop thinking in terms of intervals, and directly write out the list of working versions as something like [3.2, 3.3]? With an optional upper bound for known non-working versions: [3.2, 3.3] && <3.4. Of course we could hardly get rid of the current version bounds syntax altogether, but it might be interesting to consider what could be done to support this pattern.

2 Likes

Another way is to write a blacklist instead of a white list. I know it doesn’t work with this this and that.

I think you can already write (== 3.2 || == 3.3) && < 3.4, although I’m not sure about the parentheses because they aren’t documented.

But that only permits exactly 3.2 (not even 3.2.0) doesn’t it? IIRC that’s what ^>= was introduced to tackle.

But in general I think you do want version ranges, because the PVP is supposed to tell you, “If 3.2 works, then 3.2.8 will work as well.” Of course, nothing except hard real-world experience can tell you if that’s true. But if we don’t accept it as a possibility, and instead require our users to only use our specific version bounds, it would be impossible to ever build any package due to conflicts. Users would be stuck immediately ignoring all those specific bounds.

The funny thing is that if you do have hard real-world experience, version bounds are unnecessary. And if you don’t have hard real-world experience, then version bounds are at best a “possibility”. It does throw the whole delicate scheme into question. (And yes, this debate has been trod many times before. Stackage is one answer to the question. Another potential answer would be even more automation that can help packages give better guarantees about their dependency constraints.)

1 Like

Note that we already have a set notation:

^>= {3.2,3.3}
6 Likes

Yeah, and there’s also == 3.2.* if you do know that 3.3 doesn’t work.

1 Like

This one is not in the docs for build-depends!

Edit It actually is! Sorry for the false alarm.

It is, actually. :slight_smile:

I might… just… rewrite that section…

2 Likes

Oh wow, it is! Now I see it!

I think that the chronological presentation threw me off. My bad.

I like that line of thinking, and suggest to go even a step further: Don’t write the list of working versions, but gather it from your test suite. Assuming that “working version” can only mean “tested version” (because else you don’t know), you can fully generate the build-depends version from your CI tests:

This tool creates dependencies like text ^>=1.2.4.1 || ^>=2.0.1 for you. You no longer have to think about how to write your build dependencies – instead, you think about which configurations to test on on your CI.

2 Likes

Maybe it could generate set notation instead :stuck_out_tongue_closed_eyes:

Yes, easily. I think I didn’t do it because the notation doesn’t buy much when you are generating it manually anyways, instead of having to write it, and I was worried about cabal version backward compat. But maybe there is no reason to support pre-cabal-3.0 here anyways? It doesn’t matter much either way.

It’s more that I haven’t read everything from the Cabal User Guide, and hadn’t encountered this operator.
I’ve seen it pop up in some libraries on hackage, which is why it’s not completely new to me, but I just didn’t think too much about it.
“Must be up-to-the next (minor) version… or something?” was mostly my thoughts when encountering it. :sweat_smile:

Never had any trouble building things with that operator, so didn’t spend any brain cycles on it :upside_down_face:

2 Likes