-
Notifications
You must be signed in to change notification settings - Fork 696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Cabal support for LTS Snapshots #7556
Comments
[admin edit, by @Mikolaj: the PVP section mentioned below has since been removed from the main post] I'd love to be able to point I agree with gbaz - I'm not sure why PVP and version bounds are being discussed here. After some thought, I'm curious if you're intending to support a
While the I can see that feature being useful as an application developer, to avoid having a As a library developer, I would not want to commit to Having a |
@parsonsmatt I removed the PVP commentary - @gbaz talked me down off the ledge 😄 |
(deleted my earlier comments since the issue was edited) I'd be against setting resolvers as a package level property, as packages can work with much larger sets of version bounds than in a single resolver, and furthermore that info would make it hard to back-into full dep sets for solving without a lot of extra fetching. Having a minimal #6528 covers remote freeze files, and i think the only slightly subtle design work is caching. The remaining aspect to bridging this is either having stackage again more prominently expose its freeze files, or creating a more general I'd also like to improve hackage-server support for storing and serving package sets, which is some design work but maybe a gsoc worth of code (at most). |
My understanding is the same as @gbaz: resolvers should be a project-level property, not a package-level property. This would also be consistent with how Nix and Stack work |
At a high-level I think something like this would be quite useful for all the reasons you've listed; I don't really have much to add to this other than to be another voice of support. Thanks for starting this conversation and writing up the RFC!
Do you think it would be useful to have something like |
It'd be nice to have a list of resolvers that I intend on supporting, with version bounds specified by those resolvers if not otherwise present. This would make it easier to have accurate version bounds when trying to support a wide range of dependencies. |
Having support for fixed/curated package sets is certainly something I support. And I've mentioned this a long time ago, we could support this almost today if we allowed hackage-overlays to not only augment hackage, but also restrict/constrain the available package set. I am just a bit confused about this statement?
|
👋 Just a +1 from me a user of stack. I think with this change I would eagerly switch back to cabal and I suspect many other stack users would to. I think cabal getting more users is good for cabal. As an aside, in the long term I think you could even consider discontinuing stack in favor of cabal with this key addition (and perhaps a few others). Obviously that is a much broader discussion, but I think it would be good for cabal and Haskell more broadly. |
Aren't these already supported by downloading the correct |
@parsonsmatt can you expand on how this might work? Like you could have a set of resolvers and CI would check against all of them? I imagine this would be maybe a twofold thing. First, make it easy to pass different resolvers on the command line, and second then teach the CI script to run a project multiple times, one with each setting... I was just looking at a related ticket (#7367) on generalizing freeze file handling to decouple it as directly from |
As an industrial user of both stack and cabal, I am in full support of being compatible with Stackage. I am truly happy to see that the new Cabal team is motivated and dynamic on the subject of improving the tooling. |
Obviously, I support the idea of remote freeze files. However, I'm mildly against adding direct syntax support for stack resolvers, whether it's
One problem with remote freeze files is the lack of support for pinning revisions. This can currently only be done implicitly via hackage-index, which is not the same as stackage snapshots. This needs discussion. |
An issue if we want a stack-like workflow is that the semantics of freeze files don't seem to be the same as that of resolvers. The most important difference is that in Stack user settings (e.g. This is pretty important since essentially every project I've ever worked on with a Stack configuration has needed to tweak the resolver like this in some way. So we might need some extensions to freeze files or the solver to make this usable.
Having tried this, yes that gets you a file you can use as a freeze file, but it's not really usable for the reasons above. |
The cabal.config files provided by Stackage are not immune to revisions, which is a problem if we want to use them directly
We can have an integration of stack2cabal under the hood by |
Yes, I pointed that out above. This needs to be discussed.
Many:
|
Excellent point. Stack2cabal indeed works around this by cloning source-repository-packages and dropping the package names from the freeze file. I think our options are this:
I'm tilting towards 1, but I'm not sure I see the whole picture of use cases (e.g. source-repository-package can reference a branch, which may change the underlying package version.. in that case you'd want it in the freeze file, no?). |
It's not just In
and that all works fine. But if you write in
you will get a conflict with the constraints from the freeze file. I don't know if it's possible given how the solver works, but if it were possible for the constraints from the freeze file to be overridden by "later" constraints, I think this would solve the problem. (But arguably this is then the wrong behaviour for a "freeze" file, which should force you to take exactly the constraints it specifies. So maybe "freeze files" are the wrong vehicle for this, and we want something more like "default constraint files".) |
Excellent points. I've given this some thought as well and I think we might wanna reconsider our approach. Adding more files will be confusing for new users, IMO, because the interaction needs to be documented:
I believe this is too much. Instead I propose to redo how we handle project files. This may be a breaking change. Include DirectivesSpec
Example
As such, a This will need some thought about semantics of the lock file, about fetching, when to update the lock file etc etc. Also: instead of relying on order, we could add a |
I don't think this works because it's the wrong kind of overriding. The You can see this today: put And you certainly don't want a |
Yes, constraints will have to be merged. I don't see the problem. |
iirc --allow-newer=somepkg (and/or older) followed by --constraint=somepkg==x.y works (not sure if it does in project files too) |
To re-iterate my comments on IRC:
hvr worked hard to keep the runtime of no-op invocations of I would really like to hear how any possible remote resolver/freeze file design plans to deal with those issues, because if adding snapshot support breaks these usecases that'd be a big regression in |
We already discussed this here: #6528 (comment) |
I agree with @merijn about this should be opt-in: what snapshot will be used as default, stackage, a hackage new one to be added, one setup by the user? Hard to make happy all users. |
Note that if we implement this though the intersection of two features (freeze files (or something like them), and remote freeze files (or something like them)), then users who care about performance have a simple option: pull down the remote file, check it in, and just use it locally. Since this is a |
I have an old RFC on this topic: https://mail.haskell.org/pipermail/cabal-devel/2015-July/010214.html |
And just to reiterate the obvious points:
Nice to have (and the subject of the RFC above): a common format for expressing and distributing collections. Obviously one would want to be able to convert fixed collections (like stackage) into this format. But it's nice to have a format that anyone can create, host, distribute. Nice to have: distribution via hackage (as well as private local or http) so distribution is open to anyone. |
One important consideration here (and something not covered in my old RFC) is that many use cases need more than just unions of sets of constraints. Sometimes one needs to be able to override/replace rather than just union. So one thing I played with a little in the past was a little algebra for combining collections, with both union and override. One could invent a syntax for that to use in the cabal.project file to combine named collections. That would let you do things like say "I want that LTS version with these local tweaks". Another good thing about this approach is it might be able to replace the existing mechanism where hackage trustees tweak dependencies. Instead of tweaking .cabal files the constraint adjustments could be one of these package collections. |
I'm not sure what is the difference to my proposed include directives, where you would do:
|
There's a lot of reinventing the wheel going on here. The existing Supporting Stackage snapshots directly is by far the easiest thing. The relevant parts of I don't know if re-using a a) extend the It's not at all clear to me that extending the Implementing the support for Stackage resolvers directly would be excellent UX for cabal users. I would love to be able to have, as an example:
@gbaz With With a Even now |
I don't think so. We're trying to find a solution in the design space of Cabal that fits it. Just adding features from other tools in the same fashion will cause regressions in overall UX.
As explained earlier, it is not easy.
Stackage resolvers aren't special. They are just a set of constraints. We should support constraints better (remote constraints in particular). Whether that's a stackage resolver or something else doesn't matter. Cabal shouldn't assume use cases. So I think either include directives (more powerful) or the RFC from @dcoutts (a bit more specific to constraints) is the way to go. |
This thread started with "Let's support
"Upstreaming a library" has always been easier for me than "redesigning and reimplementing all of that library's features."
Both of these suggestions are far more complicated with far worse UX than a simple If you want a complicated way of specifying these things, you can just use |
I agree, but are we re-inventing stack here or are we trying to improve cabal? I mean, as you say about nix, it already exists. |
At least from my perspective there's something to be gained from "cablifying" the concept of a resolver. It seems to me (possibly wrongly) that a resolver just is a set of overridable constraints (i.e. constraints which can be "beaten" by conflicting user constraints). This lets us embrace the resolver workflow by adding smaller, more orthogonal features to cabal, namely:
And there's no reason why we couldn't also have a I think this is nice if what we want is the stack-resolver workflow, rather than just the feature exactly as-it-is-in-stack. |
@parsonsmatt what I sketched above would let one set different files -- in fact, we can set different On the broader stuff, I agree with Michael and Duncan. As I said, 95% of the way there is remote freeze files, and those are very straightforward to implement, involving teaching cabal about a new flag, but not a new way of fetching, nor a new format, etc. As others have noted, that doesn't quite suffice because we want some overridable syntax for bounds. But I think such a syntax is a good idea anyway. And again, extending that syntax is a pretty clean and straightforward path once the design is nailed down, leveraging a ton of code that already exists. I think we can decide we do want overriding and merging in this thread, and have a separate discussion to nail down the exact best design to do so (I can think of a few alternatives, including some perhaps not even listed here). Vis a vis the concern from @merijn:
I discussed in that ticket a number of approaches. Most fundamentally, we can use etags and http date checks to avoid refetching of unchanged files. (I.e. use http's on built in mechanisms for caching). Furthermore, I think its reasonable to only check for changed files on configure rather than install. And finally, since users have to specifically request a resolver, this behavior would only occur if they did so. Finally, I think the revisions question can be ignored in the first pass, and we can work to resolve it if/when it becomes an issue. Index-pinning is a very good approximation, and should suffice for most scenarios. |
I think there are two ideas about this, one being new syntax in the freeze file for the constraints, such as I believe interpreting sets on the tooling side is a good idea and allows us to gradually implement these things. The next step could be naming sets, fetching multiple freeze files and then have an algebra on how to combine them in I may also point out that there's a subtle difference between freeze file and constraint set: freeze files can contain everything |
@hasufell indeed I think it's very similar in concept. |
At a high level, I would just say:
|
I don't think making it the default in a compatibility-breaking way is even on the table. For making it the default in what |
It sounds like there's overwhelming support for this, and I'm happy to include it for the Cabal 3.8 release! |
I'd like to sketch a slightly modified version of @hasufell 's proposal, based on some irc chat:
And that's it! For now we leave .freeze and .local semantics as is -- we could add more configuration in a future release. This means that we can therefore omit the This only leaves the question of the combining stuff. But if we consider the use-cases, there's only two that matter in my opinion. First: some default package set that we may want to override (i.e. bumping the bytestring version). Second: pinning down a specific version for freeze purposes. If we think in terms of "and" and "or" the first scenario is "union with or" and the second is "union with and". But both cases are covered by simply having the semantics be "always override." For version constraints, each declaration of new constraints would simply replace the old constraints. For flag constraints, we would not override on a per-package basis, but a per-flag basis. I.e. "-foo" would override "+foo" but "-bar" wouldn't clear out any existing "foo" flags. This should avoid needing to introduce any explicit package algebra for now, or modifying existing constraint syntax. As far as I know it should not harm any existing behavior, and it gives us a default that is easy to understand and which obeys least surprise. (edit: the one use case this doesn't cover is "negative collections" of blacklisted versions, etc. that said, the preferences and deprecation mechanism on hackage give us at least a single "global" way of warning off certain packages. I think the negative collections idea is nice, but should be considered as a future extension of this work, since they're not the main initial use case we want to target, and it simplifies things considerably to just not worry about all the "algebra" questions of combining for a first cut) Thoughts? |
I'm a bit unclear on what the behaviour of overriding sets of constraints is supposed to be. Let me list out some options that I see and see if that clarifies things.
That is:
would give us just
That is:
would give us But what about this case? (ref: "adding inequality to existing equality")
If this gives us just
The general case here is where the "overriding" constraints for a package However, I'm not sure it's easy to actually specify what this behaviour should be, so maybe it's not worth it. We could try and say something like:
or something, but quite possibly this has ugly corner cases and might be hard to compute. Anyway, I think the "adding inequality to existing equality" usecase is worth considering, but it's probably fairly niche and shouldn't dictate the design.
Doesn't it? I guess it depends what you want. I think the design you proposed (assuming we're overriding per-package) would let you give a set of blacklisted packages, but allow a user to override them, i.e.
would give us |
I meant 2 -- full overrides per package, since I think the behavior will be clearest and easiest to implement. As for blacklists, yes. this will give us what you describe -- overridable ones. I suppose that's ok, although I worry a bit about the UX experience. |
FWIW:
However https://www.stackage.org/lts-18.8/package/nix-paths-1.0.1 is in Stackage LTS 18.8 (GHC-8.10.6) I was really surprised to find this, but it makes a point: Stackage should be test-build with Somewhat related: Stackage snapshots record (also automatic) flag selections, which breaks across different environments (operating systems). I actually have no idea in which extend Stackage snapshots are built on Windows (and MacOS), and how these differences are expressed. Anyway, the collection grammar should be expressive enough to allow flipping flags in different (staticly known) environments. (or people have to rely on automatic flags being selected correctly, which should be the case if the branches are mutually exclusive, which is the design guideline but not enforced - paranoid people may still want to pin the automatic flags too). |
related: #7783 This covers the ability to include freeze files, other project files, etc in project files. Which leaves only the constraints stuff, which really should be a distinct PR. |
Hi, is this feature still being planned for 3.8? I'm stack user who is eager to switch to cabal if this support is in place. |
In 3.8 we will have conditionals and includes (including remote) in cabal project files. So it will cover many use cases. However, we will not yet have the ability to override constraints in included files. So if you include a given lts, you will not be able to bump the version of anything in the lts, outside of downloading and modifying its freeze file, then depending on the modified freeze file. There's a design for changing the semantics of multiple constraints on the same package to allow later constraints to override earlier ones, but it will not make it in time for 3.8. |
@gbaz you write:
Is there a ticket describing this feature request and / or design you mentioned. Should the current ticket be closed as completed in its basic form? (The ticket title reads: “support for LTS” not “support for overriding constraints from LTS”] |
Continuing from this discussion here: https://twitter.com/bkmlep/status/1427696844662919169
What is this RFC for?
Discussion amongst the Cabal team has led us to consider the idea of supporting Stackage Nightly snapshots and LTS (Long Term Support) releases. This issue should serve as a preliminary request for comment and the start of a design document for supporting Stackage LTS snapshots and features associated with it.
Why should I care?
LTS snapshots support closed universes of package sets that are guaranteed to work together. Many of us already work with Nix, which relies on Stackage to inform its stable package sets. Working with Cabal + Nix is already implicitly working in an LTS. This would be a good feature for Cabal to support, due to many users strictly consuming dependencies, in which case they do not care in particular about setting bounds, which is a feature for library maintainers and consumers who need finer grained dependency management.
With #6528, we could support LTS snapshots as remote freeze files, and allow users to consume other kinds of stable package sets.
When will this go in?
At least 1 year (Cabal 3.10), at most 2 years (Cabal 3.14)
I like this, how can I help?
Speak up on this issue, or get ahold of one of the Cabal team on
libera.chat#hackage
The text was updated successfully, but these errors were encountered: