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
Fortified language editions #636
base: master
Are you sure you want to change the base?
Conversation
Maybe the same issue as discussed at https://mail.haskell.org/pipermail/ghc-devs/2024-January/021490.html. |
You can see it in the readthedocs build at least https://ghc-proposals--636.org.readthedocs.build/en/636/proposals/0000-language-editions.html |
Thanks for writing this up. I have many questions. Many of them may The "Proposed change specification" does not define a language edition. I think you intend us to inherit an ituition from Section 1 Motivation, but the proposed change spec should stand on its own. We also have a brand new concept of a "bundle". Apparently a bundle can control extensions and warnings. So it is kind-of-like a language edition. But not really, I think. For example, I think (but I am not sure, it's not specified) that a language edition can change what is in Let me note that
Questions that don't seem to be answered by the spec.
What is the purpose of having a default language?
"The one restriction on the expressive power of language editions is that build products of different language editions must be compatible." We need to be very careful with statements like this:
Let's spell this out, lest anyone misunderstand. "Stable2024: Code compiled in the Stable2024 edition will be expected to compile (assuming stability of libraries) for 6 years, until the beginning of 2030." I assume, though you do not say this, that you mean that This is a bold claim, and one that I do not expect to hold in the near future. This is an aspiration (BG1) laid out in GHC Stability State of Play but there are multiple reasons why it is hard to achieve, including reinstallable Let's not make guarantees that we cannot (yet) keep. One particular point on which the proposal is silent: does the language edition fix the "Although language editions have wide authority, we must be tasteful in how they work." This is (I think) discussing the quesiton of whether it is OK for
I'm still baffled about I suggest omitting this. Just stick to the point that warnings can change with language editions. "Once e.g. Stable2027 is released, new language features will not be available with the 2024 editions." I don't like this at all. Why break exising programs, especially when the whole purpose of langauge editions is to allow you to say "just use the 2024 language version please"? |
proposals/0000-language-editions.rst
Outdated
some of the language editions I'm proposing: | ||
|
||
* ``Stable2024``: Code compiled in the ``Stable2024`` edition will be | ||
expected to compile (assuming stability of libraries) for 6 years, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on line 19 it says "3 years"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also maybe it's worth clarifying that "GHC releases until 2030 will continue to support Stable2024", since it's really about GHC releases and not the date when the code is compiled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was confused by this as well, but I think it's consistent: you have 6 years of stability, so after 3 years a new edition comes out and then you have 3 more years to migrate.
arguments that precede it. | ||
|
||
- A cabal file will allow a new | ||
field ``language-edition``, available both at top-level and in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use default-language
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FAQ: #636 (comment) :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the answer to the proposal.
+-----------------------------------------------+-------+-------+------+-----+------+----------+--------+-------+--------+--------+-----+---+--+------+--------+--------+--+--+--+----------------------------------+ | ||
|``RoleAnnotations`` | | | Y | Y | Y | X | | | | | | | | | | | | | | | | ||
+-----------------------------------------------+-------+-------+------+-----+------+----------+--------+-------+--------+--------+-----+---+--+------+--------+--------+--+--+--+----------------------------------+ | ||
|``Safe`` | | N | N | N | N | | | | | | | | | | | | | | | [#]_ | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Safe Haskell is not compatible with Stable? Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because there have been increasing noises to remove the feature.
Broadly in favour, although I think the proposal is too broad and could be significantly trimmed down.
Simon says
Agreed, we need to make sure the details of how this will work are hammered out and we're sure it's feasible before we can commit to stability.
This needs a clarification, I agree. I would state it with a different emphasis: we must not change an extension's meaning for a given language edition, but we could change the meaning in a new language edition. We might well decide that changing extensions from one edition to the next is too confusing to contemplate (and I would agree!) but given that in the past we've felt it necessary to change the meaning of extensions from one GHC release to the next it seems plausible that we might want to make changes in the future. We could decide that those kind of changes can only be made by adding a new extension, which would be arguably better. |
Thanks for the detailed feedback!
I'm not sure what you mean here. It appears to do this to me, with sentences like "Every file is compiled with respect to precisely one language
I've added some text drawing out the distinction between an edition and a bundle.
Yes this is a downside. I've addressed this in the new FAQ section.
True, but the premise of the hypothetical is false. That is, we have found the need to evolve the definition of language extensions somewhat often over the course of developing GHC. With this proposal, we actually make this easier -- as long as we preserve the old behavior with the old language edition.
I believe I have now answered these. See my recent diff.
A good question. I've changed this a bit to say that you don't need to specify a language edition if compiling a bare Haskell file.
Indeed. I've clarified this a tiny bit in the main specification and then expanded on this in the FAQ section.
I've clarified that this covers multiple GHC versions. But I stick by the claim. Firstly, it says "assuming stability of libraries", letting the stability of base to be resolved separately. But then it also says "expected": that is, users should expect this. We won't always deliver what they expect, but we should be rightly apologetic when we fail to deliver.
Here you offer an expansion of "tasteful" -- but the expansion carefully does not offer any more guarantees than I did, with words like "usually" and "almost always". I've expanded this a bit in a FAQ, but I think expanding on this point in the specification is too much of a digression.
I like the text there. One point that's perhaps too inferred is that the
I continue to like this. There's nothing backward incompatible about it: it's just saying that users will have to upgrade to 2027 in order to get features invented in 2028, say. I've clarified the text a bit.
Maybe. But the way in which these are all tied together is that I want to change the user's view from "I have to think about all these extensions" to "I have to choose an edition and a few toppings." That is, I'm pining for a future where the average Haskell programmer does not even know about extensions. They just have a language that stably evolves!
I like this text you've written and added it to the proposal. Thanks! |
One thing to consider here is that we have some settings off by default in GHC, eg, I recently ran into this and though it was a bug: https://gitlab.haskell.org/ghc/ghc/-/issues/24025. I think it's reasonable that we have these off by default. The average user doesn't need to see the full gnarly details of the type/kind system. But, as it stands, it's also difficult to discover this flag, and I assume there's others like it. Perhaps this should be included in the Another thing in this area is: https://gitlab.haskell.org/ghc/ghc/-/issues/21031. Namely this error message is suggesting enabling a somewhat advanced feature, but if we know that a user is a |
One of the proposals is for ``Stable2024``, which will continue to be supported, | ||
without change to the set of user programs accepted, for at least 3 years. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this say "6 years"? That's what the remainder of the document seems to suggest.
One of the proposals is for ``Stable2024``, which will continue to be supported, | |
without change to the set of user programs accepted, for at least 3 years. | |
One of the proposals is for ``Stable2024``, which will continue to be supported, | |
without change to the set of user programs accepted, for at least 6 years. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good point.
I can see a merit of I'd suggest to introduce I feel fairly strongly against providing variety of |
I like these ideas a lot. For me, by far the most important language edition is |
Just to stress again: currently we don't have a way to promise that old code will compile with a new compiler, because of (a) Template Haskell and (b) lack of reinstallable base. (Are there any other blockers?) That remains our aspiration (see (BG1)); and language editions helps us along the way (a little bit -- our existing extensions mechanism is almost as good, except for rare cases when we change the meaning of an extension in a non-back-compatible way). Overall, though, I feel as if we have 10km to go, and this proposal moves us on 1km. Helpful, but we should not let our users believe that language editions will do more than they do! |
Yes, agreed. Perhaps I should have said "code that compiles with that setting will not fail to compile in the future because of changes to the language specifically". GHC can't guarantee that code will compile in the future; it can guarantee that if code doesn't compile it wasn't because of a language change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @goldfirere for all the effort you've put into this proposal. While I don't agree with the direction you set out, I'm grateful you are driving this conversation forward, and I hope we can find common ground. To try to summarize my position (and with apologies if I've missed anything while reading the text):
- The overall impression I get from the proposal is that it is much too dictatorial. It is full of restrictions that exist not because the combination of features cannot be supported, but because as language designers we have decided to make them harder to access. It is very "all-or-nothing": either opt in to the proposed stable feature set in its entirety, or choose an entirely experimental option.
- In practice, it seems like it will be very common for code to mostly conform to the stable subset, then have a few modules where something more complex happens. I think we should give users the ability to say "most of my code is stable" and make selective exceptions where they are willing to take the risk. Otherwise many packages will not be able to benefit from stability guarantees, or will need to be contorted to receive them.
- More generally, I think we should help our users to make good decisions about which combinations of features are suitable for their use case, but trust them to do so and give them flexibility to override the defaults where they have reason to do so. We shouldn't assume we know better than them about which features are suitable for their use case based on a few very broad generalisations.
- Bundling extensions/warnings into larger combinations that make sense as a group seems like a reasonable idea in principle, though the details of the bundles need some thought, and I think we should do this only where there is a clear reason to enable (and document!) them together as a unit. (For example
DoSyntax
is unconvincing: just turn on e.g.Arrows
individually!) - I'm skeptical of the complexity that arises from having extensions/bundles mean different things depending on the language edition.
- I think we should include features that are not language extensions/warnings in our classification of "stable" vs "experimental" features (see Which GHC features are experimental? #635). For example, some optimization flags are clearly experimental, and some are clearly more stable. But it isn't obvious to me that tying this to a language edition makes sense.
- I don't think it is feasible to agree this proposal in time for GHC 9.10. Thus I suggest we release GHC 9.10 with
GHC2024
as already agreed.
* A new warning | ||
``-Wmissing-language-edition``, will inform users | ||
that they should specify a language edition. This warning will be | ||
off by default in GHC, but it is expected that cabal will turn it on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cabal packages already specify a language edition in all cases (usually via default-language
, but if that is not specified, then Haskell98
- see haskell/cabal#9668). So I don't think we need this part about Cabal turning on the warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That ticket still has some debate about this point. I favor keeping the text as written in this proposal. If cabal in practice always specifies a language edition, then turning the warning on always would have no effect, anyway.
language edition will be ``GHC2021``. | ||
|
||
* A new warning | ||
``-Wmissing-language-edition``, will inform users |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #632 I suggested calling this -Wunspecified-language
, and put it in -Wcompat
. I don't really care which name we pick, we should just end up landing on something consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I've added a note about this in the Alternatives.
A: Because of the word "default". That word implies that this is the | ||
language for all modules except those that specify some other | ||
language, but I think the cabal file should be authoritative about | ||
language edition. That is, I want to be able to know that a package | ||
is stable just by looking at the cabal | ||
file. Maybe this is problematic if someone really wants a package | ||
mixing language editions? I suppose if that is a use case we want to | ||
support, there could be ``language-edition: | ||
as-specified-in-each-module`` or something. Alternatively, we could | ||
say this field is not required by cabal. Regardless, I don't think | ||
the field should specify an overridable default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is extremely unconvincing, in my view. So if someone wants to use an experimental feature (say DefaultSignatures
) somewhere in one module, they have to mark the entire component as experimental? I realise you want to move away from the fine-grained story we have at present, but this seems unnecessarily restrictive. Moreover it generates busywork for Cabal. Keeping default-language
and allowing it to be overridden on a per-module basis would be much better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could agree with the move not to introduce busywork around the naming of the flag. But actually I think it's problematic to be able to override the language edition locally. If I see a Haskell package whose cabal file says Stable
, then I want to know that the package really is stable. So I stick by the text I've written above, including the proposed escape-hatch of as-specified-in-each-module
(or something -- I actually think my proposed UI here is poor).
- Any new language features invented once e.g. ``Stable2027`` is released | ||
will not be available with the 2024 editions. That is, if we introduce a new | ||
feature ``-XDependentTypes`` in 2028, then enabling ``-XDependentTypes`` | ||
with ``Stable2024`` (or even ``Experimental2024``) will be an error. | ||
This policy encourages users to upgrade their editions in order | ||
to access GHC's new features. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it will be really annoying if one has a large package using Stable2024
, and want to start using features from Stable2027
in some modules, but Stable2024
-> Stable2027
is not a seamless upgrade, and you can't vary editions within a single component...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm.... interesting point. Yes I think it would be nice to allow this. Specifically, I could see allowing a language-edition override if the overriding language edition is a later version of the same series. In your proposed scenario, the cabal file would say Stable2024
(meaning: this package will continue to compile until at least 2030), but individual modules could have Stable2027
(meaning: this package will continue to compile until at least 2033). This works because the stability guarantee of Stable2027
is strictly stronger than that of Stable2024
. It doesn't work as nicely for overriding a Stable2024
promise in a cabal file with an Experimental
module.
How does this sound? I went to just incorporate this into the proposal, but it's not all that easy to do so, as doing it right probably requires having a notion of Stable
separate from 2024
. (This separation has been suggested elsewhere, too, but I didn't see what new flexibility / expressiveness the distinction granted -- until now.) I'll leave this as an underspecified Alternative for now, but if we end up settling in agreement here, I'll rewrite the proposal accordingly.
Unlike language editions, a user may specify any number of bundles | ||
when compiling a file. In order to simplify their processing, a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can a bundle be specified in a .cabal
file? Is it possible to negate a bundle (e.g. -XNoFancyTypes
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be nice to specify a bundle in a .cabal file yes. Maybe also -XNoFancyTypes
? I guess -XNoFancyTypes
means that individual modules would be unable to turn on the features in there? I'll add this as an open question -- could be convinced in any direction here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess -XNoFancyTypes means that individual modules would be unable to turn on the features in there?
Ideally not: this is inconsistent with how extensions work and in conflict with the general principle that you should always be able to override earlier stuff.
It is time for the ``Safe`` ecosystem to be dismantled. We should warn | ||
on the use of any of the ``Safe`` language extensions, but otherwise | ||
ignore them. With the introduction of the language editions proposed | ||
here, GHC will allow a ``Safe`` module to depend on any module, thus | ||
allowing for a transition period, even if some libraries eagerly adopt | ||
these language editions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I think I said before, while I agree with this, I think it needs more than a footnote.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This proposal does not propose getting rid of Safe
. It just doesn't include Safe
and friends in the various language editions.
* ``Stable`` and ``Experimental`` are central to this proposal; ``Student`` | ||
and ``Latest`` less so. I can see forgoing either of these two. I like | ||
keeping them, but maybe others disagree. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see very little reason to have Latest
. We moved away from -fglasgow-exts
a long time ago. Users can ask for the specific experimental/latest features they need, rather than getting an ad hoc collection of most of them.
I don't have a clear opinion about Student
, although it feels rather arbitrary to me. I think it may be more confusing than helpful to have the available language features differ from Stable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't much care about Latest
. I still think I like it (though it has gotten a negative reception) -- mostly because I want to move away from worrying about extensions at all, and then Latest
just gives me all the good ones. :) I think a key difference between Latest
and -fglasgow-exts
is that we should feel free to evolve Latest
arbitrarily.
For Student
, I think the real power is in the ability to tailor error messages appropriately, though perhaps that is not enough of a motivation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all Student
amounts to is more verbose error messages, then I don't see why that couldn't just be a flag. --include-error-explanation
. Or perhaps if we're learning from Rust we just want ghc --explain <error-code>
?
- A cabal file will allow a new | ||
field ``language-edition``, available both at top-level and in | ||
build-product stanzas. This will specify the language edition. To | ||
support backward compatibility, this will use the ``default-language`` | ||
setting if that is available, and omitting the ``language-edition`` will | ||
use the default. At some point, it is expected that ``language-edition`` | ||
will become required. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a process point of view, we can't definitively specify Cabal features like language-edition
here, though we can certainly suggest them to the Cabal developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed absolutely.
There are some things I like about the proposal, namely the concept of language editions with the potential to encompass the built-in libraries, and the aspiration to improve the forward compatibility. There are others that I'm very suspicious of, like the feature bundles. I concur with the existing opinions that the proposal should be broken up so we can discuss and vote for each aspect separately. Re: bundlesUnless I missed something, a bundle would in effect be no different from a language extension that implies a bunch of other extensions. We already have those, as well as a process for adding new language extensions. If you want a new language extension named |
@TeofilC I'm sympathetic here, and I agree that these flags are hard to discover. I started to write some notes in the proposal for how it might help this situation, but I ended up stopping: I don't quite think @Bodigrim I think we need some alternative to @simonpj @tomjaguarpaw Yes this only affects GHC, not the libraries. Does this go far enough to be worthwhile? I think that's a little bit the wrong question here. In order to achieve the goal of having Haskell code live for a long time, we need a stable GHC and stable libraries. Fixing either one alone won't deliver the goal. But certainly the fact that one alone doesn't deliver the goal shouldn't stop us from fixing one alone, because that approach gets us nowhere. As to whether GHC or the libraries are the primary culprit in instability, I think the answer depends strongly on whom you ask: library folk tend to say that GHC keeps changing under their feet, and GHC folk say the libraries keep moving around. It doesn't matter in the end: let's fix both, which we're endeavoring to do. @adamgundry Thanks for the detailed feedback! Very constructive.
Yes! That's the intent! :) I've come to think we've been doing our users a disservice by giving them fine-grained control over details many of them won't have mastered. We still should give control to users who want that (this proposal does not suggest actually removing the language extensions), but the default should be that we, the experts, can lay out reasonable choices that should suit most use-cases. Of course reasonable people will differ here, but if you're calling this dictatorial, then I've expressed myself clearly. :)
That's intentional, too. Stability is all-or-nothing. That is, either the language is stable, or one aspect of it might change -- one aspect is enough. The plan here does give users a way out: they just have to say
That might be true. Maybe, then, we should imagine settings in cabal that say "mostly stable", which allows individual modules within a package to use experimental features, where the maintainer of the package is responsible for being stable-in-practice. Yet this is problematic, because now if I want to depend on such a package, how do I know whether to trust the maintainer's commitment to stability? We'd need a network of trust, not unlike what we have with I think, in the end, we need to be prepared to give something up, in the name of stability. Or, rather, package authors need to -- it would be their choice.
That's absolutely true. But we likely do know better than users which features are more stable. If they want to go beyond that subset, by all means they should -- just by saying
Yes, I may have overreached here.
Why not? A language is a syntax + static semantics (type system) + dynamic semantics (run-time behavior). The optimization flags control an aspect of run-time behavior. Seems like part of a language to me. (To be clear, I see why some would differ here -- I'm trying to take an opinionated stance.)
Yes I tend to agree. I don't think this should be rushed.
The parts are designed to make for a cohesive whole -- including the bundles. The main payload in that "cohesive whole" is a movement away from thinking about language extensions (whose consequences for maintainability, training of new developers, stability, etc., are hard for most Haskellers to keep up with) to just a few language editions and bundles, which can be understood more easily. The committee may wish to partially approve this proposal, and perhaps they'd slice this in a way that preserves the intent. But I do not see myself taking the time to make this revision before a vote, say.
A bundle can do more than just enable other extensions. It can change warning settings and, potentially, the text of error messages. (As usual, we do not formally treat error messages in these proposals.) While it's true that I could propose each bundle separately, I think they make the most sense taken together, and with the language editions. In the past (specifically around changes to type variable scoping), we've had a bunch of separate proposals. They all were trying to talk about some of the same things, though each was notionally independent. Then I was asked to coalesce them all, so they could be understood together; this is #448. Then, in the debate for #448, some comments said that it's all too big and we should consider the pieces separately again! (Those comments did not carry the day.) I conjecture that this kind of reformatting is not a good use of our time. Instead, nothing stops the committee form partial acceptance, if that's where things are headed. |
---------- | ||
|
||
The primary motivation behind the use of language editions is that they | ||
can succinctly inform GHC what kind of user it's faced with, so GHC |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the focus on the user is good, but what @tomjaguarpaw 's comment points out is that we haven't worked out what the (sorry) user personas are. What does the user of Stable2024
want or expect? In particular, I think there are a range of possible expectations:
- About their own code:
- Is guaranteed to be stable on later versions of GHC
- Is likely to be stable on later versions of GHC
- Can/cannot have those guarantees reduced if they make exceptions
- About their dependencies:
- No assumptions
- All/some are guaranteed to be stable on later versions of GHC
- All/some are likely to be stable on later versions of GHC
Here are some possible combined positions:
- "I want tools to help me ensure that the code that I write will be stable on later GHCs, but I want the ability to take things into my own hands and make exceptions if that is useful. I want to be able to communicate stability levels between packages, so that I can have a level of assurance that my dependencies are stable, and communicate my own stability promises downstream; but I am okay with there not being hard guarantees (since I expect everyone to make exceptions here and there)"
- "I want everything to be rock solid. I have no interest in using cutting-edge features at all, and will jump through as many hoops as necessary to avoid it. I also really don't want to deal with dependency issues, so I only want to use dependencies that are rock solid as well"
- "I mostly don't care that much, but it would be nice if the defaults pushed packages towards using more stable features so things were less likely to break"
Similarly for students:
- "I have no idea what I'm doing. I just want it to work so I can clear this class. I'm not going to do anything the teacher doesn't tell me to and I want no surprises."
- "I love Haskell! This is great! I immediately want to play with all the toys! It's day 2 and I'm trying to implement an iceland_jack tweet!"
I'm unsure who the users of Experimental
or Latest
would be. I guess people who don't care about stability at all. But most people care a bit. In particular, here is one more persona I think we might want to worry about:
- "I really need to use
LinearTypes
. That's kind of the point of this project. I know that opens me up to a bit of breakage, but that doesn't mean I want everything to break. If anything, I want to keep the rest as stable as possible to compensate for my big stability hit in usingLinearTypes
!"
The reason that I think this is important is that while I appreciate the desire to be opinionated, I think there's a risk that we fixate on a particular manifestation of the "cares about stability" persona or the "is a student" persona, and that can also be pretty harmful to the other people!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this really helpful.
For the different stability goals: I continue to claim that Experimental2024
is a viable "escape hatch". Maybe we just need TryingToBeStable2024
which is technically identical to Experimental2024
but socially distinct? (That's a bit of a joke.) That is, in order to suit the "rock solid" camp, we need Stable2024
to be completely reliable (or as close as we can make it). The other stability folks can work with Experimental2024
and social cues. I do like the LinearTypes
example. But that author still has to make a choice: do they want rock-solid dependencies (Stable2024
) or not (Experimental2024
)? Anything beyond rock-solid requires the social cues again.
I continue to become less enthusiastic about Student
. I agree that the error-message aspect could be controlled using a different, less heavy-handed mechanism.
For Latest
, the persona is simple: "I just want to experiment with Haskell. I'm not going to publish this." Except that maybe they do, in the end, for use by other Latest
people.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the different stability goals: I continue to claim that Experimental2024 is a viable "escape hatch".
I agree this is fine if we have per-module language editions (although I continue to be confused about how that works given that specifying the edition twice is an error and GHC can have some flags that it must process before seeing the module).
But that author still has to make a choice: do they want rock-solid dependencies (Stable2024) or not (Experimental2024)?
Here again we're talking about dependencies. I think this is worth making really explicit because I think it needs quite a different story. The workflow for making my code stable is straightforward enough: I set Stable2024
as the language edition. But how am I going to know that my dependencies are stable? I don't see how you can provide any actual guarantee there without doing something like Safe Haskell. So it's really quite important to the scope of this proposal a) whether saying anything about dependencies is in scope, and b) whether we want hard guarantees or only social guarantees; all of which depends on which personas we want to satisfy. So I would really like to know your answers!
For Latest, the persona is simple: "I just want to experiment with Haskell. I'm not going to publish this."
Why would they not use Stable
? "I'm messing around" is a reason to not care, not a reason to actively desire Latest
. If the people who don't care will be satisfied with Stable
, that's simpler. So who wants Latest
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an excellent point. This proposal offers no enforcement mechanism around dependencies. I think it probably should. Specifically, I think there should be an option stable
, specified in a cabal file, that requires all dependencies to also be stable
. It would be an error to specify stable
and not have a StableXXX
language edition selected. For simplicity, I'm suggesting here that the cabal flag not specify the year; that can be done in the language edition.
- A cabal file (or individual module) can specify
Stable2024
without specifyingstable
. That's fine. It means that the package or module is locally stable but not part of the guaranteed-stable ecosystem. - This plan is much simpler than the plan around Safe Haskell, because there is no escape hatch. (Cue the tomatoes, which I deflect below.) That is, there is no equivalent to
Trustworthy
. Simple straightforward transitive dependencies. No exceptions. - (Here is where I deflect tomatoes.) The reason this works is that we can all program without the use of unstable extensions. Look at the "N"s in the
Stable
column in the proposal. The key question: can we live without these extensions? In particular, can we compile major packages (includingbase
!) without these? Let's look.
These N extensions affect only concrete syntax; other concrete syntax is easily available: AlternativeLayoutRule
, AlternativeLayoutRuleTransitional
,CUSKs
, DeriveAnyClass
, NondecreasingIndentation
, NPlusKPatterns
, NumDecimals
, OverlappingInstances
, OverloadedRecordUpdate
, ParallelArrays
, RelaxedLayout
, TransformListComp
, TypeAbstractions
These N extensions do nothing or are subsumed by other extensions: AutoDeriveTypeable
, NullaryTypeClasses
, TypeInType
These N extensions actually do increase expressiveness, but in ways we actively do not want: DatatypeContexts
These N extensions actually do increase expressiveness, but we have better ways to say it now: DefaultSignatures
These N extensions are properly experimental: LinearTypes
, RequiredTypeArguments
, StaticPointers
These N extensions are part of Safe Haskell: Safe
, Trustworthy
, Unsafe
Safe Haskell aside, the question really is: do we want stable packages to depend on other packages using DatatypeContexts
, DefaultSignatures
, LinearTypes
, RequiredTypeArguments
, or StaticPointers
? Those are really the only extensions in question. DatatypeContexts
have been deprecated for more than a decade. That said, they're not unstable; I could see allowing these in Stable2024
if we want to extend the deprecation period. (Though, actually, I think DatatypeContexts
can be perfectly simulated by pattern synonyms. So maybe we could indeed excise them from the language.) DefaultSignatures
are more controversial. We should probably do a little impact study to see how hard it would be to get rid of them (in favor of the more flexible DerivingVia
). I'm content to allow these into Stable2024
. The other three really are experimental, and I don't think we should yet allow them into a stable
ecosystem. (TypeAbstractions
is both experimental and has syntactic workarounds.)
Safe Haskell is a different ball of wax. Maybe the best approach is to deprecate them, but still allow them in Stable2024
, albeit that they would have no effect and cause warnings.
The bottom line for me here is that I think we can make this stable
ecosystem without need a trust mechanism. It's the trust mechanism that makes Safe Haskell hard. It's necessary there. But I think it's not necessary here.
So who wants
Latest
?
I do! :) That is, I'd specify Latest
in my little Haskell experiments. But I easily admit that I don't need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A cabal file (or individual module) can specify Stable2024 without specifying stable
That's great. That's the way I'll use it (and the way I expect the vast majority of users would want to use it)
I think there should be an option stable, specified in a cabal file, that requires all dependencies to also be stable. It would be an error to specify stable and not have a StableXXX language edition selected
To what extent is this a GHC concern? The cabal team are welcome to implement that, of course, but that's quite independent of what the GHC steering committee decides (unless you're suggesting some sort of joint proposal).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This proposal offers no enforcement mechanism around dependencies. I think it probably should. Specifically, I think there should be an option
stable
, specified in a cabal file, that requires all dependencies to also bestable
.
What problem is this intended to solve, and for which class of users? How often are users accidentally depending on libraries that break because they used one of these extensions?
My impression is that the main reason dependencies lead to stability problems is not principally due to language changes, but library changes (and #636 (comment) seems to back this impression up). So having a stable
option that increases the chances of language stability throughout the dependency stack, but doesn't do anything about libraries changing their APIs, may not have the impact we'd like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having read this proposal again, I continue to think that the basic idea of editions is a good one. But I think the current proposal is maybe shooting for too much.
Let me propose a much more cut-down version.
Language editions are cut based on years. They are implicitly stable: they don't change. Thus GHC2024
simply means "Haskell as compiled by GHC in 2024", forever (until it is retired). These are the only language editions. If there is no language edition specified, then the latest behaviour is used and a warning is emitted. As in the current proposal, language editions can in principle affect (almost) arbitrary behaviour in GHC. In particular: which extensions are allowed (stable ones), which are default, etc.
(Exception: behaviour that is not visible to users can change. In particular, a given GHC should produce interface and object files that are compatible with each other no matter the language edition: it would be very bad for language edition to dictate interface file format! This probably applies to other non-visible behaviour too...)
Language editions can be specified more than once, the latest takes precedence. (This adds some complexity in flag processing, but it's hopefully okay: we just have to do an initial scan to find all the language edition flags and pick one before we start on the rest.)
A module can be marked as Unstable
. This gives access to unstable extensions and flags, whose behaviour depends on the current GHC. It does not unset the language edition, which still controls the meaning of stable features.
Stability information for a module is written into the interface file for a module. This is:
- Stable: if the module was compiled with a language edition, and not marked as unstable, and all of its dependent modules are stable.
- Unstable: otherwise. (Maybe we add a
Trustworthy
-like override)
(This omits a remaining source of instability: the removal of language editions eventually. We might want to also record the oldest language edition used transitively in order to spot this?)
A warning is emitted when depending on an unstable module.
(A variant: record a list of the transitively used unstable module, allow whitelisting some of these. Might be useful if some core library has an unstable part that renders everything depending on it unstable)
I think this covers all of the users!
- "I want absolutely everything to be rock-solid/I'm a textbook writer": use a language edition, make the warning for depending on unstable stuff an error.
- "I want to make my code stable, but I'm not dogmatic about it": use a language edition, mark stuff Unstable as needed, tell people from the first group to go away if they complain about your modules being inferred as unstable (or join them)
- "I don't care, whatever": don't use a language edition, scatter Unstable around freely, turn off the warning for depending on unstable stuff
- "I'm a normal student": use a language edition, don't use Unstable, turn on the flag for more verbose errors
- "I'm a keen student", see 3
I also think this is simpler. Editions are always stable, they always just reflect a snapshot in time, that's it! I think this is nicer for GHC devs too: you can have one check that amounts to "are we pretending that the time is earlier or later than X?" which you can use to guard every behaviour, rather than having to separately reason "should this go in edition Y?" on a case-by-case basis.
We have just one override mechanism for getting out of the stability where you want to.
The complex bit is the Safe-Haskell-like stability tracking of dependencies. I think we need this for the rock-solid people, and I think it's easier than Safe Haskell since it really should be perfectly inferrable: you just need to look at the flags used in a module, no trickiness with looking at the actual code!
I also could say more about feature bundles, but TBH I think they should be in a separate proposal. I think they're interesting in this context because they're another example of why bundling behaviour together in various ways might be useful, but I think we could decide on language editions independently, and frankly I'm already out of energy just from thinking about editions!
|
||
* **Simplicity**. The goal is that a vast majority of our users will be | ||
able to specify a language edition, and that's it. No extensions. No | ||
warning flags. This simplifies what a user needs to think about when |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"No warning flags" seems optimistic. My experience is that people have a lot of opinions about what warnings they do and don't like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimistic: yes. But I actually think it would be interesting and worthwhile to understand what warning flags are needed. That is, enabling a warning moves responsibility from the programmer to the compiler (e.g. the programmer no longer needs to worry about the completeness of their patterns), or establishes some property of a program (like the ability to find a binding site of a variable using a simple analysis). So if users are enabling more warnings, I'd want to understand what property they're worried about, so we can perhaps design our system better. That said, we probably won't do this (it's hard). I'm fine with warning flags. I just think that each warning flag is a small message that we've slightly mis-designed something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if users are enabling more warnings, I'd want to understand what property they're worried about, so we can perhaps design our system better.
This sounds good in the abstract but I'm not sure I buy it in the concrete. Consider unused variable warnings. Users want them on when they want to spot any little thing that might be wrong with their code, or tidy it up for others to read. Or, at other times they may not care about such things and want to turn them off. I don't see a way to design the system better here: users just care about different things at different times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @michaelpj here. Warning flags are useful because they allow the compiler to offer a menu of different analyses with different trade-offs, so the user can choose which they need at a particular point in time (which may well change depending on where they are in the development cycle of a particular program, or just depending on what they are currently interested in doing). This includes warnings about essentially stylistic or otherwise controversial choices (e.g. Wx-partial
), where the alternative is for the compiler authors to be forced to pick a side in the controversy (either by making a warning mandatory or by not supporting it).
|
||
- A language edition can be specified using the extensions syntax, by | ||
passing e.g. ``-XStable2024`` on the command line or putting | ||
``Stable2024`` in a language pragma. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not an extension (which it very much seems like it isn't), then I think it should be set differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I find I agree here. I will change this.
* In GHCi, the default language edition will be the latest ``Stable`` | ||
edition. ``-Wmissing-language-edition`` will be off by default. | ||
|
||
- In GHCi, the top-level can have a distinct language edition from loaded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is again an "everywhere" vs "local" clash. If we want a language edition to be like having a different compiler, it should always load files following its own expected edition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow here. A key requirement is that the language editions are inter-compatible. So having GHCi be able to load code from other language editions seems both easy to support and useful, to me.
(or existence) of other flags can depend on language edition. While | ||
we will not implement it this way, we can imagine that GHC becomes | ||
a set of programs that happen to share a binary; the choice of which | ||
program is chosen by the language edition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we see an implicit lean towards "everywhere"
The one restriction on the expressive power of language editions | ||
is that build products of different language editions (within the | ||
same version of GHC) must be | ||
compatible. We expect the Haskell ecosystem to contain packages |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we see an implicit lean towards "local"
Unlike language editions, a user may specify any number of bundles | ||
when compiling a file. In order to simplify their processing, a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess -XNoFancyTypes means that individual modules would be unable to turn on the features in there?
Ideally not: this is inconsistent with how extensions work and in conflict with the general principle that you should always be able to override earlier stuff.
|
||
* E: the warning is an error by default | ||
|
||
* W!: the warning warns by default and cannot be turned off |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will be unsurprised to hear that I hate this idea :) The user should always be able to change the behaviour if they want. Telling them they can't (when it is clearly possible) is mostly just insulting and aggravating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this particular user has asked for this restriction, by saying Stable2024
. If they want to disable the warning, just stop asking for the restriction, by saying Experimental2024
.
Do you hate that Haskell rejects the following program?
stuff = [3, "a", 10, "b", 21, "c"]
odd acc1 acc2 [] = (acc1, acc2)
odd acc1 acc2 (x : xs) = even (acc1 + x) acc2 xs
even acc1 acc2 [] = (acc1, acc2)
even acc1 acc2 (x : xs) = odd acc1 (x : acc2) xs
(sum, letters) = odd 0 "" stuff
Executing this is "clearly possible": it results with sum
being 34
and letters
being "cba"
, if I haven't made a mistake. But of course GHC will reject. And we want GHC to reject, because we have opted for a statically typed language. Similarly, a user opting for a stable language should be able to get what they want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to use Experimental2024
. I want to use Stable2024
with the warning turned off, which is a) pretty reasonable, since it clearly doesn't affect what the compiler can do) and b) something that I am used to from literally every other compiler I have ever used.
Similarly, a user opting for a stable language should be able to get what they want.
What has stability got to do with warning flags? Or being a student? Turning off a warning does not make my code unstable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If, for example, there was a warning whose behaviour was unstable, then perhaps we should not allow turning that warning on in Stable
code... except again, we have disclaimed warning stability as a goal.
|
||
* W!: the warning warns by default and cannot be turned off | ||
|
||
* E!: the warning is an error by default and cannot be turned off or made into a warning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
when it is appropriate to migrate to the new style. | ||
|
||
Includes the following: ``FieldSelectors``, ``NoPolyKinds``, ``StarIsType``, ``-Wno-deriving-typeable``, | ||
``-Wno-prepositive-qualified-module``, ``-Wno-type-equality-out-of-scope`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's interesting that this is the only bundle that includes both extensions and warning settings. If it were not for this I would be inclined to say that maybe what we want is warning groups, e.g. so you could write -Werror=incomplete
and toggle all the errors in the "incomplete warning group" into errors. I still think that's a plausible alternative, though - if Classic
is the only example of such a bundle then maybe it would be cleaner to have extension groups and warning groups separately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe. But I continue to think that the division between language extension and warning is very arbitrary, and so separating out these two from each other seems unhelpful, to me.
``RequiredTypeArguments``, ``RoleAnnotations``, ``TypeAbstractions``, ``TypeData``, | ||
``TypeFamilies``, ``-Wterm-variable-capture``. | ||
|
||
* ``DoSyntax``: This enables extra syntactic support around ``do``-notation. Someone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the stability story for bundles? Presumably if we add a new kind of extension that affects do syntax, then we would want to add it to DoSyntax
, but we don't want to change the meaning of old code. So I think the language edition must also control the meaning of bundles?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course! But I agree that this is not spelled out in the proposal. The proposed bundles all have the same meaning regardless of the proposed edition. This might not be true in the future. I will clarify.
The issue of stability has been one of the primary goals of the GHC maintenance team over the last few years. We now have extensive regression testing of the compiler against a large set of packages, including memory performance and several important library testsuites. This has also led to a real culture shift within the framework of GHC development where we are constantly assessing whether and why any breakage should happen. I think the proposal needs to tackle the implementation question much more seriously. At the moment it states:
The implementation cost of the proposal stretches much further than implementing the editions as described in the proposal. The cost only begins at this point, it is then for the next 10 years that ALL changes to the compiler must be analysed against ALL existing language editions to decide whether ANY change violates the strong contract expressed in this proposal.
The proposal also side-steps the issue of library stability. It seems that you have to also at least include the stability of the base library in such a proposal for it to have any impact. If someone is able and willing to modify their program to reflect changes in In my opinion it's really important to consider these kinds of questions when discussing stability as stability as a goal is hollow without the resources to back it up over a prolonged period. |
Some thoughts:
|
One other question. I think it is arguable that:
If we intend these properties to hold, this proposal would be a good place to articulate them. |
I'm glad that there's enthusiasm in the community for increasing the stability of our language, libraries and tooling. I think that is an important component of making Haskell more suitable for wider adoption. Stable language editions can contribute to the goal of stability but I have concerns about the particulars of this proposal. Firstly, I'd like to challenge the notion that GHC per se is a major cause of instability in the Haskell ecosystem. This is an empirical issue so we ought to look at what has happened in practice. In fact I don't think we can improve the state of stability in our ecosystem without a careful empirical analysis. Stability is not the kind of issue we can improve through abstract technical definition. Improvements have to follow from practical observation. Here's an example: @angerman has written a helpful list of everything that broke when upgrading a codebase from 9.2 to 9.6. Spoiler: it wasn't GHC's fault! The breakage was caused by
Only one of the five issues arose due to a change in GHC, and that was only a warning, so not actually a breaking change according to the GHC's proposed stability goals. Here's a second example: a GHC 9.8 breakage inventory, of all breakage that I could discover that users had experienced when upgrading from 9.6 to 9.8. None of them are to do with changes to GHC per se. They are all related to libraries which are bundled with GHC. As a third example, I am currently migrating a codebase from 8.10 to 9.6. With the exception of shallow subsumption, which I easily avoid by using So GHC per se is simply not currently a major cause of instability in the Haskell ecosystem. The major cause is the package ecosystem. Now, GHC does exacerbate the problem: it forces an upgrade of Secondly, I'd like to challenge the notion that enforced stability will improve the situation. To me the idea that using I would strongly advise against looking at stability from the rigid point of view that we use when we look at mathematical proofs and type systems. Stability is a systems engineering concept, not a mathematical concept. If we try to shoehorn it into a rigid mathematical framework then we won't end up with a form of stability that serves us, instead we will serve stability. My suggestion would be to go ahead and define a stable subset of Haskell that GHC commits to supporting for a prolonged period, call it |
I think you are right, but I would love to hear from others on this point.
Yes. To be specific:
Neither of these conflict with this proposal; but when it comes to where to invest scarce cycles, both are probably more important :-).
I agree. This is what I described as "nanny-ish" above. Let's not. |
I disagree.
|
Firstly, perhaps I used terminology in an unclear way. By "not major" I didn't mean "not important" nor "should not be improved". I meant that such breakage only comprises a small minority of the total breakage in the ecosystem. Perhaps that resolves the disagreement, but if not, then secondly, it's not really a subjective matter where people can agree or disagree. It's an objective empirical matter borne out by practice. Maybe GHC is a major contributor to instability, but that can only be demonstrated by real world observations. The three data points I provided are hard for me to reconcile with GHC being a major contributor to instability but perhaps those data points are not representative. If you can share any real world experience reports of all the breakage that happened during a major version GHC upgrade that would be very helpful. I think it is very important to determine the major causes of instability and primarily focus on improving those first. Naturally, different streams of work can happen in parallel, but if we expect to be able to improve stability without careful analysis of real world outcomes then we are likely to be disappointed. |
My experience is the same as yours. The GHC bits aren't where I spend time. With one notable exception: the |
But that wasn't a GHC change per se, was it? |
Debatable 🙂 . I'd argue it was: the desugaring of the “do” notation changed, and that's largely what caused issues. |
Ah yes, desugaring a partial pattern match picked up a |
I think the cause of disagreement is that you apply open-source lens, where indeed ecosystem churn is a bigger contributor. What I am talking about is an industrial setting, where I can happily fork pretty much every package (e. g., stick to |
Yes, that's right. I was including open source packages on Hackage in my assessment. I'm still not sure I fully understand the stability criteria that are important to you, because although you can indeed insulate yourself from breakage in packages by not upgrading the packages you can also insulate yourself from breakage in GHC but not upgrading GHC. If you want a new feature in a newer package you'll have to take the risk of breakage from the upgrade; if you want a new feature in a newer GHC you'll likewise have to take the risk of breakage from the upgrade. In fact the waters are quite murky because not only can packages make it look like GHC is unstable when it's not (because they don't bump bounds to work with new In any case, this seems all rather marginal because from #636 (comment) I understand that you and I want the same thing. |
Hi, Overall I like this idea, here are some things that stood out to me. From the proposal, it's not clear to me for whom the target audiences are for the various introduced language editions At one point it mentions it shouldn't recommend datakinds to students but that's a bit strange to me. Especially, students have all the time in the world to figure out typelevel programming. You completely lost me at semantic bundles 😅 Why is this included? I think it's a great idea to introduce warning control into language extensions. This would make upgrading easier because I clearly remember having to go through an entire warning phase after having solved all library compilation issues or stuff related to simplified subsumption. (In commercial code base you'd have -Werror enabled on CI). This would've prevented that. I'm sorry if I repeated comments, I don't have time to read through all discussion |
@jappeace The proposal answers your question for Student2024:
|
@Kleidukos what's the difference from stable then? |
Just to clarify one point, the above sample was what I consider light breakage, it still took many hours to get sorted properly. Other changes have been much more impactful. Especially if you are trying to upgrade early after the release. I can't stress this enough: If the next compiler does not accept the same code as the previous compiler, we completely fail at quality control. I cannot run regression tests against 9.10 today. And production the production codebase is stuck on 8.10 (with compatibility up to 9.8 almost everywhere) due to performance regressions since. Not sure if I should cry or laugh at this. Also The bigger issue though is that because GHC itself normalizes this, it does not set the necessary precedence for downstream. If everything upstream is broken all the time, this signals to downstream that breaking stuff is perfectly acceptable. As a downstream maintainer, what does it buy you to try and not break things, if upstream breaks stuff anyway? As I've mentioned elsewhere, I do consider |
@angerman The CLC governs the maintenance of the mtl library, the impact of the You are a member of the CLC so it seems that if you disagree with the sentiments on this ticket about why upgrading to mtl-2.3 was the right thing to do then it would be best to raise it on that forum. Likewise, if you object to breaking changes which are approved the GHC steering committee (which you also a member), then GHC developers can do nothing about that, so you should raise an issue there to discuss that. I don't think it is very useful to make broad sweeping statements such as "everything is broken all of the time", that's not actionable, vague and shows a level of disrespect to GHC contributors who have spent a significant amount of time on stability related issues in the last few years. Creating "perfect" software is an impossible goal which no-one should be held to. One can only hope to incrementally improve things over a sustained period of time. It's very demotivating to actually put work in to improve the situation for those efforts to be ignored or discounted regularly and publicly by prominent members of the community. |
@mpickering you will notice that I am doing precisely that since I am on the CLC and SC. I try to be very consistent in this. I will however not refrain from pointing out issues that there are. Yes, we are making progress, and I'm not denying that. Pretending there currently are none would be wrong. We need to acknowledge the defects there are so we can work towards resolving them. If you think I'm discounting efforts, you are misunderstanding me. If you think I should not highlight issues that exists today, because these issues might be resolved in the future, we disagree on that. |
We've discussed that in the ticket you linked already:
I don't think there's a point of making GHC HQ responsible for the whole "GHC bindist" experience. We rather need to move away from the whole bundling business and establish better boundaries and "pipelines". E.g. bundling is ultimately a job for distributors (stackage is a bundle). |
Been a busy few weeks, followed by a holiday. Back in action here. Re counterproposal: Interesting. To me, the key difference between what you've written here and what I've proposed is that your design includes stability information in an artifact, something I had not considered. It allows stability to be opt-out, instead of opt-in. That is, you can have a package be stable "by accident", just by choice of dependencies and language extensions. I think I'd rather stability be opt-in, though: this means that stable package authors are aware of the commitment they are making, leading to a lower likelihood of a package switching from stable to unstable in a future version. The rest of the counter-proposal just looks like a small UI change: separating the year of the edition from the stability level. I think doing so is sensible, but I don't yet see an advantage to it. I could easily be persuaded here. This counter-proposal also suggests yearly editions, which I think would increase the burden on GHC maintainers. Have I missed something here? Yes, all good points. I agree completely that the burden on implementors is long and enduring. My estimation is that the burden will not actually be all that big (that is, a small ongoing tax), but I could easily be wrong here. In answer to your direct questions:
I'm not imagining any new mechanism here. The existing (nicely beefed up!) procedures will work: the testsuite, head.hackage, early beta testing, code review, etc. The new bit is, essentially, how to react when an incompatibility arises. When we intentionally or unintentionally change behavior, a little debate arises, asking whether we should go with the new behavior or return to the old one or make a proposal or add a warning, etc. With the new stability guarantees in place, this debate will be simpler: we will retain the old behavior with old language editions and get the new one with new language editions. Implementing both behaviors is indeed a burden, but I don't think we'll need new process.
Part of the normal code review process. User-facing changes are usually evident in testsuite changes. A good reviewer already routinely considers whether these changes are good or bad. This new guarantee just informs that assessment. I don't see this as being burdensome.
A valid question, but I don't think a single individual can be expected to be the stability steward. This is a shared responsibility, just like making sure we don't accept the program It's hard to know how best to handle this ticket vs #635. On the one hand, it's nice to have separate places to debate separate ideas. But on the other, I think this proposal would be pretty meaningless without the big table categorizing everything. I think the bundles could somewhat usefully be separated out from this proposal. Yet I favor keeping them in, at least until this is submitted to the committee. My reasoning is that, in this proposal, I'm trying to change the way that GHC and its community thinks about language extensions. Up until now, extensions have been a primary part of GHC's interface. I am trying to relegate them to an advanced feature, where more users interact with editions and bundles than extensions. About the nanny state: It would be problematic if there were no way out. But there is! Just say
I disagree here; I think it contributes to this goal by giving a mechanism for change. That is, suppose we find a flaw in the instance resolution algorithm in an obscure case with In some sense, I agree with you in that this proposal doesn't change our aspirations toward stability nor gives us new resources. But it does propose a way to resolve stability-centered debates, which I believe will be helpful.
This is intentional. I agree that we'd need stability in the way you say for this to really deliver. But thinking about language separate from libraries is something we can easily do, and so I've decided to keep these concerns separate. I'll add some text to the proposal about this. Note that the fact that an edition can control all aspects of GHC means that the aspirational goal you seek is technically allowed here. I plan on adding some commentary about transitive stability, in response to some comments from @michaelpj. I will make sure to reference GR1-3.
I'm not exactly sure what this means. There's not a notion of a stable extension beyond inclusion in a Out of time now. More later. |
@goldfirere I find this very scary. This implies we have Extensions behavior depend on the language edition. This means it adds to cognitive load when reading code. You not only need to understand the enable language extension, but also it's behaviour wrt to a specific language edition. I'd much rather see Language Extensions evolve with versions. This should not put a dent into Language Editions itself much. But will provide explicit clarity, instead of implicit. |
The main thing I'm trying to do is thread the needle between the different user personas I'm thinking about:
I continue to think the current proposal is a bit of a muddle on this front, and I think a guiding light of "language editions are local, stability information is global" might help.
Is the case you're worrying about something like this?
That's a worry indeed. I think you're right and we probably need a marker to say "check that this module is transitively stable". How about
Plenty of design wiggle room here, but I think something like this could work. I do think it would be helpful to have this distinction on a per-module basis - I wouldn't be surprised if our old, tricky core libraries in particular end up with some stable and some unstable modules.
I also propose having no other language editions (except "no edition, YOLO", aka
versus
The former approach seems significantly easier for GHC devs to me (I agree a lot with Matt that we have to make this not too painful). Similarly, if language extension behaviour changes between editions, then users have to do this logic in their head, per-extension. Today, language editions can change behaviour between GHCs, so users still have to do this reasoning, but based on only one variable: the GHC version. My proposal retains this property, but instead the reasoning is based on a different single variable: the year of the language edition. We can cope with "FlexibleInstances changed slightly in GHC 9.8", I think we can also cope with "FlexibleInstances changed slightly in Stable2024", I'm not sure we can cope with "FlexibleInstances changed slightly in Stable2024, Student2026, and Experimental (in GHC 9.8, not earlier Experimentals)". This is also my response to Moritz's comment that it's scary for language extensions to change based on edition: we already cope with language extensions changing based on GHC version, so I don't think this is any worse. But we shouldn't make it worse by expanding the dimensionality of the edition matrix. Please let's stay 1-d!
The negative space is meaningful. I'm proposing to do a lot less. Drop the feature bundles, drop the mandatory warnings, just have a single big number that controls the behaviour of the bits of the compiler that affect whether your code compiles. |
I'm also skeptical that we should make extension behaviour depend directly on language edition. But I think versioning extensions provides a way forward here, because the language edition can select from the available versions of each extension. And equally it will be clear when the extension version has not changed between language editions, which is not clear if the meaning of the extension varies depending on the edition. Suppose we call what we have This should work particularly well in cases where we are adding clearly new functionality to an existing extension (e.g. the planned additions to |
I think i have some ideas for some reasonable reduction of the proposal, but i am not sure if i fully understood all the details and implications of the current state of affairs compared to the proposal.
It would be great if someone could confirm whether this list is correct and complete (@goldfire ?) If the above is correct, my thoughts are: Language editions (as proposed) are nearly identical to bundles, except they are exclusive, can forcefully prevent a feature from being enabled/disabled and allowing control of unspecified other behaviour of GHC. Further thought in this direction would be to figure out which extensions are unlikely to change in a backwards-incompatible way in the future (call these stable extensions, e.g. DeriveFunctor, but probably also stuff like ExplicitNamespaces under proposal #581?) and which extensions might reasonably change in such a way (call these unstable extensions, e.g. OverloadedRecordUpdate). This classification (stable/unstable) should be noted in the documentaion. One can probably suggest a similiar system for warnings. I apologize if this is to much of a wall-of-text/clutter, i am new to contributing to this kinda stuff. |
I ended up thinking about this some more: An (potential) additional benefit of the above procedure is, that it can easily be split into smaller proposals that (i think) each have merit of their own: Proposal 1: Add bundles that bundle together warning flag and language extensions
Proposal 2: Explizitly annotate extensions and warnings as (un-)stable and allow declaration/inferring of packages as (un-)stable
Proposal 3: Redefine language editions |
@jannikloehn Thanks for thinking this through. I agree that the best way forward here is to make incremental progress where we can reasonably split things up, and that is gradually happening (though the discussion is admittedly quite fragmented). In particular:
You may also be interested in Simon's stability state of play document which attempts to bring some of these threads together. |
This extracts out from #628 the main payload of fortified language editions, including concrete descriptions of some, including a
Stable2024
that should be able to be supported, unchanged, until 2030.If we can move quickly enough, this could supersede GHC2024. But it needn't do so -- this would still work after GHC2024 is released.
Rendered (GitHub's renderer seems to be broken. 🤷♂️ Thanks to @shlevy for finding a working render.)