Seeking feedback on language editions proposal

I’ve been spending a bunch of time recently thinking how to streamline GHC’s interface – mostly by trying to tame the unwieldy pile of extensions and warning flags that GHC supports. I’ve thus come up with a proposal about language editions.

The people who benefit most from this will be working Haskell programmers, not so much language implementors and designers. And so I’m posting here, where more working programmers likely lurk, seeking feedback. I think reading the Motivation and Proposed Change Specification sections will likely be enough to get the idea; the big table in the middle of the proposal is probably more detail than most people need.

This proposal is the main payload of a larger manifesto I’ve written, but there’s no need to read the manifesto unless you’re overly curious.

Happy for all feedback, either here or in GitHub!

25 Likes

I generally strongly agree with the motivations, but I find myself dissatisfied with a lot of the proposed changes.

I don’t find this reason compelling, the two occupy different spaces in my head (and in my projects). I’m not against bundling up warnings (-Wunused, etc.), but I don’t like the idea of warning-only bundles.

Not precisely “broad”, just that the access to extensions has trained me to think of them as separate “bundles” in an of themselves. These “bundles” should indeed be merged together if the compiler cannot establish proper boundaries between them.

Bundles like FFI and LowLevel are self-contained and make perfect sense. A bundle like Overload does not: I overwhelmingly only use OverloadedStrings and turning everything else ambiguous just to do that is weird.

I think of bundles as a top-down view of how extensions should have been evolving this entire time: extensions that extend ForeignFunctionInterface should have been incorporated into it over time instead of hanging around for 13 years (and counting).

What is the benefit of having multiple editions per cycle over, say, a single edition with a set of flags (--student/etc.) that restricts the choice of bundles and extensions? Bundles are strong enough to alter error messages, I would expect them to have their own safety guarantees.

I don’t know if the question of “why can’t Haskell do something similar to Rust’s epochs?” fits properly in this discussion, but when I think of editions I just think of that.

4 Likes

I welcome warmly the wish to whittle away the overwhelming worries over compatibility issues between warnings and errors, extension sets, options, and flags.

On compatibility, I’m still unsure of what to make of this as a library author. I would not want my choice of using Stable to limit my imports to those libraries also compiled with Stable, especially if its “Experimentality” is well-encapsulated by its API. I think bundles are a step in the right direction here, because while I fear that a full edition-based compilation pipeline is too restrictive, they sit at the sweet spot where a little goes a long way, in terms of conveying to the user (and maintainer!) what kind of Haskell-magic they’re allowed to perform. Some might call it overkill to call for a strict-edition-bundle-allow-list compiler pass through the entire ecosystem from their own cabal file, and I would agree, but such a tool could still be useful for directing eager maintainers upstream to help the community avoid bitrot. I cower in despair when I recompile Yesod on a new GHC and the dependency tree spits out pages of warnings from before the Semigroup-Monoid proposal. I think it’ll also reduce the amount of CPP people have to write, too.

I do hope to try out the Complete bundle someday soon. I think it will be difficult not to get carried away playing fill-in-the-blank with the language server.

You made no mention of GHC targeting WebAssembly. As I understand it currently requires a custom build, but as GHC grows its capabilities in these areas, I think we should also aim to capture this dimension of its behaviour.

I’m thrilled that you share the opinion that the Safe infrastructure has decayed beyond the point of rehabilitation. When I started using Haskell in 2015 I could already see its bones. Occasionally revising a Stable language edition and providing support with a wider range of compiler versions allows us to make stronger promises about build integrity while also blessing us with a sturdy foundation for other compiler improvements (e.g. the RTS).

As for which module gets to be the Prelude… I still think that should be a part of the cabal file, maybe under an implicit-imports: header. The only thing we can put there are modules we know about from the build-depends:. That also lets us kill off -XNoImplicitPrelude.

2 Likes

…again? And no mention of what exactly is going to replace it? Hrm:

…all those “loopholes” are still there (and more have appeared since then) - can you provide an alternative to Safe Haskell in that proposal (or in a different one)? Writing all of this:

https://github.com/ghc-proposals/ghc-proposals/pull/628

would have taken quite some time, so I find it very difficult to believe that there was only ever one thought about Safe Haskell in all that time: “just un-do it”

It already doesn’t work properly, you shouldn’t need an immediate replacement. And whatever replaces it is not going to be at GHC’s level anyway.

I like the principles behind Safe Haskell and in my view any good library will always follow them, but it seems to fail on every single level and every discussion about it is a neverending cavalcade of “deprecate this” messages.

1 Like

…you mean like some of the comments left here:

Thank you for working on this! I contribute to a large-ish haskell codebase, and upgrading GHC seems to always take a signficant chunk of time, regardless of how I approach it. My perspective is that I want older libraries that used to compile to continue to compile, as long as their dependencies remain compatible.

Here is an example of a silly thing I have to fix - head and tail are deprecated in 9.8 as they are partial (or they give a new warning, at least). One of my dependent libraries (which I don’t control) now fails to compile because it has -Werrors and -Wall enabled. Fortunately I can override cabal options and turn off -Werrors, but that isn’t the point. Code that used to compile now fails to compile.

The value I see from this proposal is to reduce the maintenance burden when I update my code to use new GHC, independent of choices made by library authors.

My pain updating to a new GHC isn’t updating my own code - its getting the 100s of dependencies to compile. In 99% of the cases, that is just relaxing cabal version restrictions (which is pretty fast). In 99% of the other cases, its adding or removing an import. In the remaining cases, its very dependent - sometimes finding where a function moved to (in the case of Control.Monad with 9.8), or even rewriting functionality that no longer exists (mtl stopped exporting some strict writer stuff I had to shim in).

Someone posted here from the perspective of a library author. I am posting from the perspective of a library consumer.

Thank you for your hard work!

3 Likes

That’s really a problem with the dependency. Library authors should be aware that GHC does not give any guarantees about when new warnings are introduced. Cabal will also warn you about this when you try to upload to hackage.

Even this language editions proposal will not change that:

We do not guarantee that all Stable code remain warning-free, especially with -Wcompat enabled.

3 Likes

That’s really a problem with the dependency.

Exactly my point. I didn’t change anything except GHC; but now my dependency fails to compile. Most of my work (when doing an upgrade) goes into fixing dependencies, not my own code.

Regarding warnings, if my example is bad, that’s on the example; but the problem (I have) still remains.

In general, I feel that with this proposal, if I have code + libraries that compiled under Stable2024, they willcontinue to compile under that “pack” for at least 6 years; 3 of those years will be when a new “Stable” is available, giving me lots of time to migrate to a new “pack” and newer GHCs.

That is simply not true in general. The scope of this proposal is limited to GHC itself. So it is really only about preventing certain breaking changes from happening. For example, I’d imagine it would have prevented the simplified subsumption situation. But this proposal doesn’t cover changes in warnings, changes in non-reinstallable libraries (e.g. base and ghc-prim), changes in Template Haskell, etc.

So this proposal is a step in that direction, but it is not a full solution to your problem.

2 Likes

Ah, so the examples in many Beginners’ tutorials are going to barf. Avoid success at all costs! (I didn’t know of that barfage because I have no intention of upgrading beyond 8.10. Ever.)

Perhaps I missed where it was mentioned when looking through all the associated documentation, but I’ve been wondering: if (Glasgow) Haskell could be nonstrict and parallel-by-default in e.g. eight month’s time, how many language extensions would be made redundant?

To me, something like -XSafe would then be superfluous because the nondeterminism inherent to both nonstrict evaluation and run-time subtask scheduling would make it practically impossible to use effects unsafely. (Of course, there would still be the option to keep using “sequential” processing, but that incurs the cost of forever-slower programs, as hardware-level parallelism continues to increase.)

So beyond improved program performance, being parallel by default could provide another opportunity to decide which extensions are worth keeping (or adding in the future).