topic of lock files and the lack thereof in Cabal […] Was this the reason to go for “git trailers”? It feels like a hack, but may have be the only reliable option in the current state.
Do you even want a solver? Do you want full determinism?
In the thread you link, multiple people have conflicting desires. I suspect that a lot of them just want to turn off the solver and pin every version, even transitively. That kind of workflow could hypothetically be supported in Renovate, but it’s not clear how, since Renovate doesn’t know about the dependency graph, so it doesn’t know what isn’t pinned in cabal.project
(which is where I imagine you’d want it to enforce pins on transitive dependencies).
Stack/Stackage offers a full solution for people that don’t want a solver. For example application authors that don’t have to be compatible with multiple versions of anything.
Pinning with Renovate
Renovate supports rangeStrategy=pin
. But my suspicion is, that it makes more sense when each dependency also specifies the exact version of its own dependencies. This is easier in languages where you can have multiple versions of the same library used at the same time.
Since build-depends
doesn’t pin transitive dependencies, this rangeStrategy=pin
might not actually achieve what people really want, which is, as previously mentioned, probably just full determinism. I’d love to hear about a potential use case for rangeStrategy=pin
, but I haven’t seen it so far. It seems to me that it often makes more sense to just use Stack/Stackage.
Widespread pinning in Hackage gets annoying
When many libraries pin, but they don’t bump at the same time, you get unsolvable build plans when packages with conflicting build-depends
get used together. So trustees would need to relax constraints for these packages. I don’t want to induce even more work for trustees. So that’s an argument against supporting rangeStrategy=pin
.
Indeterminism in Hackage culture as it exists
Since I am aiming to help existing Hackage libraries keep current, I have developed support for the rangeStrategy=widen
, which is used with ranges.
This fits the existing Hackage culture, and work with the maximum amount of library authors. widen
means that the build-depends
are kept in a format that admits a range of major versions (see earlier posts in this thread).
Admitting a version range in build-depends
is a deliberate omission of details. Similarily, if you don’t commit the index-state
into your repo, the solver has, depending on when it was invoked, a different set of information that it uses to choose an exact version. In other words, the choice is underspecified, indeterministic. But it’s deliberately the way that Hackage works.
Automated bumping makes it easier to have more determinism
With the advent of CI systems and automated bots, you might argue that we should always pin index-state
, because it adds more determinism. I think that might be a good idea. Why don’t people do it? Probably the indeterminism just isn’t enough of a problem. The ranges they use work well enough. So it’s not a bad idea to pin index-state
, it’s just that nobody invested the time in developing automatic index-state
bumping. Also, as previously mentioned, many application developers don’t even want a solver.
Why temporary constraints?
Previously in this thread, I have tried to explain why it is useful to pin the version of the dependency getting bumped. For example, in my initial message, I covered it in the paragraph starting with For maximum convenience. In my reply to blamario, I used the phrasing temporary constraints.
As previously explained, this constraint is necessary because the Cabal solver is otherwise not necessarily choosing the “new” version.
But just because the temporary constraint is needed because of the Cabal solver, doesn’t mean that this is a lock file in spirit. It covers only a limited amount of direct dependencies. And it’s not a file. It’s not committed into the repo either.
If you didn’t use the Cabal solver, of course the temporary constraint wouldn’t be necessary: Let’s say you used Stackage, but you have an extra-dep. Now, Stack doesn’t have a solver. So if you update the version of the extra-dep, and it is in build-depends
, you know that Stack will pick that version. It is committed into stack.yaml
, which is a file in your repo. When Renovate updates stack.yaml
, the version stays pinned, but now to the new version. No need for any git trailer to tell the CI system about any temporary constraint. All constraints are committed into the repo. More determinism, more specification, more bumping.
So I don’t think there is a bug here, since the solver is an integral part of working with Cabal. Cabal gives you the option of using freeze files, index-state
, --prefer-oldest
and so on.
So, since there is no bug, workaround is not the right word.
Addendum: Snoyman’s philosophy as I understand it
In the above example, where you have a library that you’re testing with everything pinned in a stack.yaml
, you might just want to remove all constraints. After all, you have already pinned the version in stack.yaml
, so you could argue that you don’t want to be making any promises about compatibility with other versions, at all. Even minor.
As far as I understand, this would be Snoyman’s philosophy on PVP version bounds:
Curation is a better solution for the majority of use cases
Curation is what Stackage does. So in other words, instead of having every project run the CI suite with every imaginary configuration, let’s just choose one specific version of every library, that all other libraries must then be compatible with. The Cabal solver is replaced by the curator and it is run once for every user instead of once for every build. It’s an actual program that the Stackage Curators run.