GHC Steering Committee seeks input from industrial Haskell users

GHC Proposal #601 proposes a lifecycle for Haskell language extensions and an associated system of compiler warnings. The lifecycle categorizes language extensions as being one of the following:

  • An experimental extension, subject to rapid evolution (e.g. LinearTypes)
  • A mature extension, to which changes are not expected (e.g. DeriveFunctor)
  • A deprecated extension, which will be removed from an imminent GHC release
  • A legacy extension, which is generally considered to be a design dead-end but that is not scheduled for removal (e.g. NPlusKPatterns)
    The compiler warnings would (configurably) warn about the use of experimental, legacy, or deprecated extensions.

The Haskell Foundation’s Stability Working Group thinks that this can solve a real need: teams of Haskell developers will be able to more easily understand the status of extensions and express machine-checked policies for CI. Furthermore, if an extension changes categories (e.g. moving from “experimental” to “deprecated”), the new warnings on a compiler update will be a good reminder to check the release notes and see what needs to be done.

The GHC Steering Committee is currently evaluating the proposal. They are interested in feedback from industrial teams - if you’re using Haskell at work, please chime in on the thread, even if your comment is just “Sounds helpful” or “Not relevant to us” (although substantive comments are of course also welcome).

15 Likes

How is mature defined? Experimental sounds unreliable so one can expect failures but, for instance, was Monad Fail mature or Semigroup changes mature?

The Semigroup change wasn’t a language extension, and I don’t think Monad Fail was (I guess it impinged somehow on desugaring but I don’t think it was an extension at any point).

In the proposal text, it’s defined as follows:

Mature extensions are considered to be finished and are not expected to undergo regular changes. These features can be used without worry of unexpected changes, and they are not known to contain serious design or implementation deficiencies. Any breaking change to a mature extension will be announced well in advance of the change being made, with a migration path provided if possible.

As @tomjaguarpaw indicates, neither of those changes were to extensions, so they would not have been impacted by this proposal.

4 Likes

Here are my 2 cents.

As an “industrial” user, the only things I care about is are

1 - can I use this extension with safely, i.e. will it changed or being or removed, in other word “will my code been broken in the future”,
2 - and to a lesser extend will I be the only one using it, making my code base “harder” to work with by newcommers.

I’m not sure that this proposal addresses any of it.

I understand experimental means it’s a play ground, stay away from it until it’s ready
Mature doesn’t really mean anything. I sounds like the last stage before Retiring or something, I much prefer Stable (as proposed somewhere). But stable is not great either because ultimately, I would like stable things to be pushed to the new language.
Deprecated … Have stuff actually been removed from GHC ? I mean properly removed because we don’t like them, not removed because they have been superseded by another one, maybe we need a Superseded statuse

Legacy. In an ideal world , new version of the language should absorb common usefull extension.
When this happen, all thoses extensions are not needed anymore (they are there by default), unless we want for whatever to disable them. So in effect the meaningful extension is the Noversion.
Let’s take an harmless extension which made it through GHC2021 NumericUnderscores.
It’s a great stuff, nobody will ever need to disable it.
If I chose GHC2021, I don’t need it. In theory we could remove this extension, but we want to be able to have NoNumericUnderscores and we don’t want to break code which implicitely uses it. That is what I would call Legacy. It’s there because in the past we needed it, but we don’t anymore. A warning to prune them would be helpful.
At the moment Legacy means CodeSmell, maybe Archaic or Antiquated would be better.

About the life cycle. If that proposal is about to reassure me, it does the opposite.
Apparently, something Mature can become Experimental again or ever Deprecated.
So I’m not sure of anything anymore.
On the other hand, I understand that stopping Mature things to elvove will result in everything staying Experimental for ever (we defeat the object the proposal all together).

Which leave me with only one alternative (if I don’t want to take any risks), stick with a language (Haskell2010 or GHC2021) which this proposal doesn’t address at all.
It might be more helpfull to have a lifecycle related to the adoption in the next language (Experimental → Candidate → Approved etc …)

Having said that, in practice I just use the extension I need, knowing that so far, nothing has ever been removed.

4 Likes

Thanks! Would you mind putting your comments on the discussion thread for the proposal, so that the committee will see them?

Is that not better to have a discussion there and then later adding a summary/consensus to the proposal thread with eventually a back link ?
I found those proposals thread usually hard to follow.

Realistically, the extension lifecycle should go from experimental->stable and that’s it.

If a user uses a stable extension they should expect that it will work out for subsequent releases, even if it becomes legacy or deprecated.

1 Like

That is the happy path, which in the proposal is experimental → mature. But deprecated & legacy are for sad paths, namely how to scheduling sunsets of some extensions.

From how I read, “mature” ones have high assurance for you in that matter. “legacy” ones may also work, but it’s also a warning to you that you might want to move away from it.

hmm, that’s probably the intention of extension sets: Haskell 98/2010, GHC 2021?

Thus my concerns that the extension sets are excluded from the proposal instead af being at the centre of it.

1 Like

To be clearer, what I meant was being legacy or deprecated is a tag associated with the path, and not the path itself. Once it hits mature and users start using it, there should not be any breaking changes even if it goes into legacy or deprecated, which means that these apis should remain there for some defined X versions from the warning until its confirmed that nobody uses it at all (and since this is hard to confirm, in practice this means nothing is removed once it hits mature).

This level of guarantee allows industry users to use what they want safely without fear of needing to dedicate time to refactor something because it has become deprecated. The industry can trust mature extensions are well designed with long lasting semantics.

2 Likes

There was an extension called MonadFailDesugaring which was added in 8.0.1, enabled by default in 8.6.1 and removed in 9.0.1 (I think, can’t find it in the release notes). I suppose it could be considered mature in GHC 8.6.1?

Maybe we could distinguish “superseded” (when there is a new extension that includes the same functionality and there is a clear migration path, like the case of MonadFailDesugaring) and “deprecated” (when the idea is flawed and/or abandoned).

Not sure this helps anyone though.

(I only skimmed the proposal, apologies if this has already been considered or discussed)

The point of superseded is that you can just disable the extension without having refactor anything, a bit like redundant imports.

About all of this proposal that might me the only one thing i will fond useful (unless we get instead a redundant extension warning)

My company uses Haskell to deploy web services. I think we find this useful for a couple of reasons:

  • we would default to only allowing mature extensions, to avoid future maintenance work. Ideally this could be changed in a per-file and per-package basis, eg if we really wanted to use LinearTypes in one module.
  • We would find where we were using deprecated extensions before they were removed, enabling gradual migration away from them.
2 Likes

You need to be clear to them that this means that mature extensions should not be changed at all or only go into legacy only, not deprecated.

It is not apparent currently that mature extensions wont get removed in future releases.

I am not sure we want to promise that, and the proposal actually says

Any breaking change to a mature extension will be announced well in advance of the change being made, with a migration path provided if possible.

If we could predict the future, then maybe we could make an absolute statement, but in light of more information, I hope we can still make any change – with due process. That’s the point of Deprecated, isn’t it?

I am not sure we want to promise that, and the proposal actually says

If this cannot be promised for certain LTS major or minor versions, as noted above by @maxigit then marking something as “mature” has no difference from being “experimental”. “High assurance” isn’t something that can be quantified or planned for.

If we could predict the future, then maybe we could make an absolute statement, but in light of more information, I hope we can still make any change – with due process. That’s the point of Deprecated, isn’t it?

Languages suitable for wider enterprise adoption often have long-lasting semantics, especially for LTS versions (up to X amount of years), and I believe we can all learn something from the Python 2->3 fiasco. This is not about “predicting the future”, this is about committing to stability for a certain amount of time that can be factored into planning and reducing the risk of using Haskell in production.

1 Like

Stability commitments are absolutely important.

Today, some extensions are known to be basically “finished”, and considered unlikely to ever change. Others are considered to be unfinished, subject to more or less rapid iteration as the contact with real users casts light on the design. It is highly unlikely that one of these “finished” extensions would be removed or drastically changed, certainly not without a long deprecation cycle. On the other hand, rapid iteration from release to release of a new feature is normal and expected.

How are users to know which is which? This proposal seeks to provide a means by which it can be communicated, and by which changes to that communication can be checked in CI.

I totally get that it doesn’t solve every problem - there are lots of things we can do to improve things! Do you think that this makes things better, though?

1 Like