1 Fortified Language Editions¶
I propose to lean more heavily on language editions. This proposal describes
how a language edition can control more than just extensions (importantly, one
can control warnings as well), and proposes several concrete language editions.
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.
This is extracted from a longer manifesto about how GHC should treat language extensions. For some introductory text describing the mindset that leads to this design, readers may want to skim the introductory sections of that manifesto. However, the effective payload of this proposal is all in this one document.
1.1 Motivation¶
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 can behave accordingly.
Though the details are spelled out below, it’s necessary to introduce some of the language editions I’m proposing:
Stable2024
: Code compiled in theStable2024
edition will be expected to compile (assuming stability of libraries) under that same language edition on all GHCs release for 6 years, until the beginning of 2030. (We may end up falling short of this expectation in corner cases, but it will remain a reasonable expectation to have.)Experimental2024
: Switching to theExperimental
series of editions gives you access to experimental features of GHC, which might reasonably evolve and break existing programs.Latest
: This language edition is the latest and greatest that GHC has to offer. Compiling withLatest
might break between releases.Student2024
: The student edition of the language has extra guardrails. Furthermore, any code written in theStudent
edition is expected to compile with all future versions of GHC for 10 years, meant to echo the expected lifetime of a textbook.
With that out of the way, here are some scenarios that the language editions model helps us to address:
Stability. Once we include a feature in a
Stable
edition, it will not change. However, that does not mean GHC cannot evolve. If we identify aStable
feature that nevertheless deserves an upgrade, we can do so, provided we continue to support the old behavior. So type inference aroundFlexibleInstances
, say, might work one way inStable2024
and differently inStable2027
. If we release a newStable
edition every 3 years, users have three years to upgrade before we no longer guarantee support. A three-year timeline is long enough that we might imagine contributors writing upgrade tools in that timeframe.Error messages. Because the language edition describes the user (very coarsely!) we can tailor error messages to be appropriate for them. The
Student
editions will not adviseDataKinds
. TheStable
editions will not adviseLinearTypes
.Warning evolution. One challenge in adding features to GHC is to know how to evolve the warning system. Does a new warning get added to
-Wall
? And what’s the deal with-Wcompat
?Language editions make these questions easier to answer: If we think that a warning should be enabled for users going forward, we can turn it on by default, but only in appropriate language editions, such as
Experimental
orLatest
.For
-Wcompat
, we add warnings that describe features that will change in the next edition in a given series. So theStable2024
edition of-Wcompat
will warn about features changing inStable2027
. This set of warnings will likely grow between 2024 and 2027; that’s fine. (We do not guarantee that allStable
code remain warning-free, especially with-Wcompat
enabled.) Now if a user is runningStable2024
in 2028 with-Wcompat
, they won’t see warnings about changes due in 2030; they’ll see just the warnings they need to upgrade toStable2027
.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 setting up a Haskell project, removing the paralysis of choice that can reign today.
1.2 Proposed Change Specification¶
We refresh the concept of language edition. Existing language editions
Haskell98
,Haskell2010
, andGHC2021
will continue to be language editions, to which we add the following:Stable2024
Experimental2024
Latest
Student2024
A language edition can be specified using the extensions syntax, by passing e.g.
-XStable2024
on the command line or puttingStable2024
in a language pragma.Every file is compiled with respect to precisely one language edition. Specifying more than one edition is an error. If a user specifies no edition during compilation, then a default is chosen:
In all versions of GHC released before the end of 2025, the default language edition will be
GHC2021
.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. In this way, someone experimenting with a standalone Haskell file doesn’t have to fuss about language editions, but anyone building a package (presumably for publication) is encouraged to choose.Starting in 2026, the default language edition when none is specified will be the latest
Stable
edition available. (The warning continues, unchanged.)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 files. If a file is being loaded and does not specify its own language edition, it inherits from the language edition in force at the top-level.
A language edition can control almost all behaviors of GHC. The meaning (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.
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 compiled with a variety of language editions, and they must work together. The word compatible above is doing some heavy lifting, in that it contains a notion that the interface (i.e. exported symbols, their types, etc.) can be translated from one language edition to another. This aspect of Haskell’s design has always been present, in that different files can be compiled with different langauge extensions. We often don’t notice this, because we have been careful with language extensions not to make the translation apparent.
Although language editions have wide authority, we must be tasteful in how they work. It would be problematic to have
Stable2024
andStable2027
disagree on the meaning of widely used features. Yet I think it’s best to be maximally expressive here, relying on our future selves not to abuse our power.(In the conversation, it was suggested that a
Python
language edition would be beyond reason… but actually I think such a thing would be lovely, if the necessary work was done to translate the interfaces between Python and Haskell.)For backward compatibility, a language edition can be specified at an arbitrary place in a command-line invocation of GHC, or in a
LANGUAGE
pragma in a file. Even though it might come later in a command line, the edition can affect the meaning of command-line arguments that precede it.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 thedefault-language
setting if that is available, and omitting thelanguage-edition
will use the default. At some point, it is expected thatlanguage-edition
will become required.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
withStable2024
(or evenExperimental2024
) will be an error. This policy encourages users to upgrade their editions in order to access GHC’s new features.Once an edition has been eclipsed by newer models (that is, once the calendar reads 2028 but someone is still using
2024
), we know that we are compiling older code. Our priority for such maintenance modes is stability, not, say, making sure that the set of warnings conforms to the latest standard.We introduce several coarse-grained semantic bundles that group together similar features. The individual choices are detailed below. When using one of the new language editions, error messages suggest opting into one of these bundles, rather than suggesting individual extensions.
The bundles described below are all just combinations of existing language extensions and warnings. I expect that bundles will remain as such (though it’s conceivable that we might imagine language features that do not get their own extension, just a spot in a bundle). It’s also conceivable that the bundles will evolve to encompass more expressive power (such as controlling optimization flags or the meaning of
import Prelude
).Unlike language editions, a user may specify any number of bundles when compiling a file. In order to simplify their processing, a bundle cannot control the interpretation of other command-line flags. Otherwise, though, a bundle can potentially control arbitrary behavior of GHC, just like a language edition. The big difference between them is that a file is compiled against exactly one language edition, but it can be compiled against an arbitrary number of bundles.
When printing out the namne of a warning flag as part of a warning, we also include any bundle that also controls the warning.
1.2.1 Detailed specification¶
The text above defines language editions generally. This section instantiates the general design with some specifics. These language editions affect language features (as described by extension name or warning flag in the chart below), as well as error messages (not described in any detail). They do not affect other aspects of GHC.
The chart below classifies all current extensions and warnings.
The GHC2021 column has an X for extensions enabled as part of GHC2021. For warnings, it lists if the warning is part of a current warning bucket.
The next four columns describe
Student2024
,Stable2024
,Experimental2024
, andLatest
, respectively. Here is the key for these columns:Y: an extension is turned on
blank: the feature is available, but not on
N: trying to enable the feature is an error
W: the warning warns by default
E: the warning is an error by default
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
The next several columns describe semantic bundles of options.
FancyTypes
: The user should enable this if they want fancy types. By enabling this semantic bundle, the user takes responsibility for understanding type inference and interactions at a deeper level. In an enterprise setting, the user takes the responsibility for training future collaborators in these techniques.Includes the following:
DataKinds
,ExistentialQuantification
,GADTs
,GADTSyntax
,ImpredicativeTypes
(allowing polytypes in visible type applications and in type parameters, no Quick Look),QuantifiedConstraints
,RankNTypes
,RequiredTypeArguments
,RoleAnnotations
,TypeAbstractions
,TypeData
,TypeFamilies
,-Wterm-variable-capture
.DoSyntax
: This enables extra syntactic support arounddo
-notation. Someone who enables this option takes responsibility for understanding the extra syntax and for training others to understand that syntax.Includes the following:
Arrows
,QualifiedDo
,RecursiveDo
Classic
: In some places, Haskell has recently evolved, and the recommendation of the new language editions are to use the new syntax or features. In a few places, users can enableClassic
mode to use older-style Haskell. A user enabling this feature takes responsibility for keeping abreast with today’s Haskell best practices and determining 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
LowLevel
: This bundle enables a suite of features that allow users access to low-level details, mainly around unlifted types. A user enabling this option takes responsibility for knowing about strictness vs laziness and for knowing about how Haskell values are represented in memory.Includes the following:
ExtendedLiterals
,MagicHash
,UnboxedSums
,UnboxedTuples
,UnliftedDatatypes
,UnliftedNewtypes
Overload
: This bundle enables more overloading of Haskell operation. A user specifying this option takes responsibility for fixing any inference failures that arise from too much overloading, often by adding type annotations.Includes the following:
MonadComprehensions
,OverloadedLabels
,OverloadedLists
,OverloadedRecordUpdate
(only withExperimental2024
orLatest
),OverloadedStrings
Sugar
: This bundle enables a small suite of syntactic niceties. A user enabling this option takes responsibility for knowing the new syntax and having any new collaborators also learn this syntax.Includes the following:
GADTSyntax
,MultiWayIf
,ParallelListComp
,PatternGuards
,PatternSynonyms
,PostfixOperators
,RecordWildCards
,TransformListComp
(but not inStable2024
),TupleSections
,TypeOperators
,UnicodeSyntax
,ViewPatterns
(I am the least confident about this group.)
FFI
: This enables Haskell’s foreign function interface. A user enabling this option will need to understand the details of the FFI to be effective.Includes the following:
CApiFFI
,ForeignFunctionInterface
,InterruptibleFFI
,JavaScriptFFI
,UnliftedFFITypes
TH
: This enables Haskell’s Template Haskell feature. A user enabling this option takes responsibility for understanding that recompilation will become more frequent, as well as understanding how staging issues can affect code reuse. The user also forgoes the possibility of doing cross-compilation.Includes the following:
QuasiQuotes
,TemplateHaskell
Unused
: This turns off a suite of warnings that tell the user when part of their code is redundant. Users enabling this option take responsibility for monitoring their own code for unused variables and other constructs.Includes the following:
-Wno-unused-do-bind
,-Wno-unused-foralls
,-Wno-unused-imports
,-Wno-unused-local-binds
,-Wno-unused-matches
,-Wno-unused-packages
,-Wno-unused-pattern-binds
,-Wno-unused-record-wildcards
,-Wno-unused-top-binds
,-Wno-unused-type-patterns
Explicit
: This option causes GHC to warn when it makes an assumption. To get code to compile withExplicit
, users must rely less on inference and more on their own annotations. Users specifyingExplicit
take on the responsibility of writing and maintaining these extra annotations.Includes the following:
-Wmissing-deriving-strategies
,-Wmissing-exported-pattern-synonym-signatures
,-Wmissing-exported-signatures
,-Wmissing-export-lists
,-Wmissing-import-lists
,-Wmissing-kind-signatures
,-Wmissing-local-signatures
,-Wmissing-pattern-synonym-signatures
,-Wmissing-role-annotations
,-Wmissing-signatures
Complete
: There are a number of places where programmers can omit parts of their program and still get it to compile. TheComplete
option makes these into errors. All such features are warnings by default (within the language editions, at least);Complete
turns them into errors. Users enabling this option will have to fix such errors long before deployment. (Other warnings require fixing only for deployment, not during development.)Includes the following:
-Werror=incomplete-patterns
,-Werror=incomplete-record-selectors
,-Werror=incomplete-record-updates
,-Werror=incomplete-uni-patterns
,-Werror=missing-fields
,-Werror=missing-methods
T1
,T2
, andT3
. These options are meant to be analogous to optimization levelsO1
,O2
, andO3
, but for type inference. When a user specifies optimization, they understand that the runtime behavior of their program may be less related to when they wrote and thus harder to preduct, but presumably will be faster. Similarly, specifying a type inference level beyond 0 means that GHC will work harder to accept their program, but the exact types inferred become harder to predict.T1
: Type inference is expected to be stable, though it is harder (in corner cases) for users to guess the inferred type. The type checker may also run forever, but it will never produce a program that does so (unless the expressions in the program indeed loop).Includes the following:
FlexibleContexts
,FlexibleInstances
,LiberalTypeSynonyms
,MultiParamTypeClasses
,TypeSynonymInstances
,UndecidableInstances
,UndecidableSuperClasses
T2
: It is possible (though unlikely) that type inference will change between major releases. This level enables functional dependencies, which allows type inference to propagate information in new, sometimes unexpected ways.Includes the following:
FunctionalDependencies
,ImpredicativeTypes
(Quick Look only),NoMonoLocalBinds
,OVERLAP
pragmas,TypeFamilyDependencies
T3
: The type inference engine is now allowed, in some scenarios, to make guesses between two valid possibilities. These guesses might even influence runtime behavior. Enabling this level of type inference should be done only by users who are confident in writing confluent sets of class instances.Includes the following:
DeepSubsumption
,IncoherentInstances
(but without implyingINCOHERENT
on every instance),NoMonomorphismRestriction
,-Wno-warn-orphans
Here is the key for the “semantic bundle” columns:
X: The feature is enabled.
O: The feature is disabled.
W: The warning warns.
E: The warning is an error.
The notes column carries brief notes. For some extensions, it says flag. This means that the extension doesn’t fit into the rubric of “taking on responsibility”, but instead expresses the user’s preference for how to interpret a program. I expect these extensions to remain as independent extensions (not bundled) into perpetuity.
Though not captured in this table, I also think that language editions should control error messages. That is, the
Student
edition might have error messages more tuned to students’ needs, over experts’ needs. I do not intend to give a specification here of the details, but it’s something I think we should keep in mind as considering all of this.
GHC2021 |
Student |
Stable |
Exper |
Latest |
FancyTypes |
DoSyntax |
Classic |
LowLevel |
Overload |
Sugar |
FFI |
TH |
Unused |
Explicit |
Complete |
T1 |
T2 |
T3 |
Notes |
|
|
flag This is a bug. |
|||||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
flag |
|||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
X |
|||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
flag |
|||||||||||||||||||
|
N |
N |
N |
N |
Deprecated. |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
N |
N |
N |
N |
Deprecated. |
|||||||||||||||
|
X |
|||||||||||||||||||
|
N |
N |
Y |
Prefer |
||||||||||||||||
|
N |
N |
Y |
Prefer |
||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
X |
|||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
Y |
flag |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
X |
flag |
|||||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
X |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
Y |
Y |
Y |
X |
X |
||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
||||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
|||||||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
X |
|
|||||||||||||||||
|
Y |
X |
Quick Look algorithm |
|||||||||||||||||
|
X |
Does not imply |
||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
|||||||||||||||||||
|
X |
|||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
Note |
|||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
N |
Y |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
|||||||||||||||||||
|
Y |
Y |
Y |
Y |
O |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
O |
||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
Y |
Y |
Y |
X |
||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
See |
||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
N |
N |
N |
N |
||||||||||||||||
|
N |
N |
N |
N |
Use |
|||||||||||||||
|
N |
|||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
N |
N |
N |
N |
X |
T2 enables |
||||||||||||||
|
X |
|||||||||||||||||||
|
X |
|||||||||||||||||||
|
Y |
Y |
Y |
Y |
||||||||||||||||
|
N |
Only with experimental/latest |
||||||||||||||||||
|
Y |
Y |
Y |
X |
||||||||||||||||
|
Y |
Y |
Y |
Y |
Ugly, but sometimes necessary |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
Y |
Y |
Y |
See |
|||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
Y |
Y |
Y |
Y |
Enable only pattern signatures |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
O |
||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
|||||||||||||||||||
|
X |
Y |
X |
|||||||||||||||||
|
flag |
|||||||||||||||||||
|
Y |
X |
Affects scoping. Own category? |
|||||||||||||||||
|
X |
|||||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
N |
Y |
X |
FancyTypes does not affect Stable |
||||||||||||||||
|
Y |
Y |
Y |
X |
||||||||||||||||
|
N |
N |
N |
N |
||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
Y |
Y |
Y |
Y |
|||||||||||||||
|
X |
X |
||||||||||||||||||
|
N |
Should probably be removed. |
||||||||||||||||||
|
flag |
|||||||||||||||||||
|
flag |
|||||||||||||||||||
|
X |
|||||||||||||||||||
|
Its own category |
|||||||||||||||||||
|
X |
Y |
Y |
Y |
Y |
Should be removed. |
||||||||||||||
|
N |
X |
Sugar doesn’t enable in Stable |
|||||||||||||||||
|
N |
N |
N |
N |
See note on |
|||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
N |
Y |
X |
|||||||||||||||||
|
X |
Y |
Y |
Y |
||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
|||||||||||||||||||
|
N |
N |
N |
N |
Use |
|||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
X |
Y |
Y |
Y |
X |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
X |
|||||||||||||||||||
|
Y |
X |
||||||||||||||||||
|
N |
N |
N |
N |
See note on |
|||||||||||||||
|
Y |
X |
||||||||||||||||||
|
||||||||||||||||||||
|
default |
N |
N |
N |
N |
Should be removed. |
||||||||||||||
|
default |
E |
E! |
E |
W |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
default |
E |
E! |
E |
E |
Will become an error. |
||||||||||||||
|
compat |
W! |
W |
W |
||||||||||||||||
|
No opinion. |
|||||||||||||||||||
|
default |
E |
E! |
E |
E |
Will become an error. |
||||||||||||||
|
default |
W |
W! |
W |
W |
|||||||||||||||
|
default |
W |
W! |
W |
W |
See note directly above. |
||||||||||||||
|
default |
W |
W |
W |
||||||||||||||||
|
compat |
E |
E! |
E |
E |
Will become an error. |
||||||||||||||
|
default |
E |
E! |
E |
E |
|||||||||||||||
|
E |
W |
W |
W |
O |
|||||||||||||||
|
W |
W |
W |
W |
W |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
W |
|||||||||||||||
|
N |
N |
N |
N |
Use |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
default |
E |
E! |
E |
E |
Will become an error. |
||||||||||||||
|
default |
Not needed with |
||||||||||||||||||
|
all |
N |
N |
N |
N |
Deprecated. |
||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
N |
N |
N |
N |
Deprecated. |
|||||||||||||||
|
Debugging aid. |
|||||||||||||||||||
|
N |
N |
N |
N |
Seems pointless. Remove? |
|||||||||||||||
|
compat |
E |
W! |
W |
W |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
all |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
W |
E |
||||||||||||||
|
W |
W |
W |
W |
E |
|
||||||||||||||
|
all |
W |
W |
W |
W |
E |
Should experiment; might be noisy. |
|||||||||||||
|
all |
W |
W |
W |
W |
E |
||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
N |
N |
N |
N |
This has been removed. |
|||||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
||||||||||||||||||||
|
W |
|||||||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
default |
E |
E |
E |
E |
E |
||||||||||||||
|
For internal use by Cabal |
|||||||||||||||||||
|
W |
Maybe add |
||||||||||||||||||
|
W |
|||||||||||||||||||
|
W |
|||||||||||||||||||
|
default |
E |
E |
E |
E |
E |
||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
all |
W |
||||||||||||||||||
|
W |
|||||||||||||||||||
|
W |
Should warn for abstract types. |
||||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
all |
W |
||||||||||||||||||
|
default |
N |
N |
N |
N |
Should be removed. |
||||||||||||||
|
Too noisy. |
|||||||||||||||||||
|
all |
|||||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
default |
Covered by |
||||||||||||||||||
|
all |
W |
W |
W |
W |
O |
T3 turns this off |
|||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
|
|||||||||||||||||||
|
default |
W |
W! |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
O |
|||||||||||||||
|
all |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
all |
W |
W |
W |
W |
|||||||||||||||
|
W |
W |
W |
W |
||||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
compat |
N |
N |
N |
N |
Does nothing; should be removed. |
||||||||||||||
|
default |
W |
W! |
W |
W |
|||||||||||||||
|
default |
|||||||||||||||||||
|
default |
|||||||||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
W |
X |
||||||||||||||||||
|
all |
N |
N |
N |
N |
|||||||||||||||
|
default |
W |
W! |
W |
W |
|||||||||||||||
|
all |
W |
||||||||||||||||||
|
compat |
E |
E! |
E |
E |
O |
||||||||||||||
|
default |
E |
||||||||||||||||||
|
W |
W |
W |
W |
W |
|||||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
N |
N |
N |
N |
Should be removed. |
|||||||||||||||
|
default |
W |
W |
W |
W |
|||||||||||||||
|
default |
E |
E |
E |
E |
|||||||||||||||
|
||||||||||||||||||||
|
all |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
O |
|||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
all |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
W |
O |
||||||||||||||
|
W |
W |
W |
W |
O |
|||||||||||||||
|
default |
E |
E |
E |
E |
1.3 Effect and Interactions¶
By leaning on language editions, we achieve the following:
Users can declare, at the top of their file or in their cabal file, that they are working in the stable subset of Haskell.
By clearly describing the stable subset of Haskell, this proposal allows us to uphold the stability principles articulated elsewhere.
If we ever feel the need to evolve the meaning of an extension, we can do so, by keying the meaning of the extension to the language edition. This allows forward evolution without sacrificing stability. (There may be some implementation duplication to achieve this. There is a cost to stability, but one I think is worth paying.)
We have a mechanism for introducing new warnings. Over the years, we’ve been skittish about adding new warnings on by default, lest they cause trouble for older programs. With language editions, we can tell whether a program is old or new, enabling new warnings only in a newer edition (if we so choose).
There is no need for a language edition simply to be a collection of language extensions and warnings. We might imagine features that just live in an edition, without a separate flag controlling it. (Indeed, this happens in this original proposal a bit, by splitting up different aspects of
-XImpredicativeTypes
.) If language editions prove popular, they open up new ways of evolving GHC without the fuss of language extensions.Coding tools (e.g. editor integrations, HLS) can choose to target, say, just the stable subset of Haskell. While support for all of the language is preferred, of course, having editions helps provide guidance to tool authors for where to focus their efforts.
The
Stable
edition is somewhat restrictive, with warnings that cannot be disabled and extensions that cannot be turned on. Yet there is always an escape hatch: useExperimental
instead. The motivation here is that we wantStable
to mean exactly that. If aStable
module were allowed to twiddle settings that violate the stability goals, then the user might unintentionally make their program unstable. By makingStable
restrictive, we force the user to be aware of the stability impact of their settings.Because a language edition can affect all aspects of GHC, it can also affect the module that is implicitly imported in Haskell files. Maybe it’s
Prelude
. Maybe it’sPrelude2024
, which might have some new exports. Or maybe importing a module namedPrelude
is something like a keyword whose behavior depends on the edition. (This way, you can effectively change the language edition for modules with an explicitimport Prelude
and still change what’s imported.) Such effects of language editions are not included for the editions spelled out in this proposal, but editions offer a tantalizing way to grow or alter thePrelude
over time.
1.4 Costs and Drawbacks¶
Reducing the effective configuration space (“effective”: my goal is for users to leverage new language editions and semantic bundles, but the old options remain) means that users will be less precise in specifying their language. Maybe a user is happy to have GADTs but not type families in their programs; the blunderbuss
-XFancyTypes
is too broad for them. Yet other languages have no fine-grained mechanism for controlling language features. I think this is a luxury we can live without, gaining more simplicity in its place.Because of the expressive power of language editions – with their ability to control all aspects of GHC’s behavior – we now have potentially an even larger configuration space to worry about as implementers. (“Does the bug happen with
-XWeevils
underStable
or justExperimental
?”) If we expected users to use language editions and the 100+ language extensions, this would indeed be problematic. But, again, the goal is to eschew language extensions for the coarser semantic bundles, reducing the space down to something more manageable.This proposal takes us further from where Haskell started, as a base language supported by multiple compilers, each of which could introduce its own extensions. While nothing stops another compiler from supporting the same language editions as proposed here, the coarse nature of them makes it harder to enter the field. Yet the reality is that GHC is the only industrial strength Haskell compiler, and even it doesn’t compile standard Haskell (because of the
Applicative
superclass toMonad
, at least).If language editions come out only every three years, then there is more pressure to get an idea into the next edition, instead of having to wait. This is a real problem. It is mitigated a bit by the existence of
Latest
, which will evolve with every GHC release (possibly even minor releases). To me, we’re striking the right balance between due caution and our desire for evolution, but individual experiences and opinions will differ.
1.5 Backward Compatibility¶
This proposal causes no change in behavior to existing programs, except for a warning that users should specify a language edition.
1.6 Alternatives¶
The current proposal makes
Stable2024
andExperimental2024
into language editions. In the future, we’ll have, say,2027
editions, too. A different way to slice this cake is to specify the language varietal and its vintage separately, via two flags.There’s something appealing about the separation, as it makes two axes navigable independently. Maybe it’s a better user interface than what I’ve proposed, but I think the payload doesn’t change: the combination of varietal and vintage would together make a language edition, and all the aspects of a language edition that I’ve described would be controlled by this little product.
Stable
andExperimental
are central to this proposal;Student
andLatest
less so. I can see forgoing either of these two. I like keeping them, but maybe others disagree.Perhaps we want to phase in the no-language-edition-specified warning.
There are a number of other proposals in this space, including the description of a
GHC2024
.
1.7 Unresolved Questions¶
What’s the right cadence for updates? This proposal assumes a 3-year cadence, but there’s no great reason to believe that’s the right one. Three years feels, to me, about the right balance between cost (each one of these has maintenance and specification costs) and expressiveness, but I can’t defend this feeling.
How soon to drop support for an old language edition? Elsewhere in this text, I assume that at 2030, we’ll phase out 2024 editions. Maybe that’s too soon.
Should specifying
-XComplete
also change thePrelude
to exclude partial functions? I think probably not – I’m worried about opaqueness introduced by having these bundles be too expressive – but it’s fun to dream about.
1.8 Resolved Questions (i.e. FAQ)¶
Q: Why can’t we just define language editions and semantic bundles as sets of extensions and warnings?
A: This proposal almost does this. There are a few places, though, where the current meaning of extensions cuts across several different ideas that might usefully be separated. (Specifically,
ImpredicativeTypes
both allows newer fancier types and also newer fancier type inference; these aspects can usefully be separated, as they have different expectations around stability.) Looking forward, I think it’s useful to give ourselves the freedom to change other aspects of GHC beyond what is typically controlled by extensions or warnings – such as error messages.In the future, if users end up largely migrating away from using language extensions and warnings and toward using the coarser interfaces (editions and bundles) introduced here, then maybe we can describe editions and bundles just as extensions, because we would have the freedom to invent lots of new extensions (e.g.
ImpredicativeTypeDefinitions
andImpredicativeTypeInference2024
) without impacting users.Q: Why not just keep
default-language
in cabal instead of changing it tolanguage-edition
?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.Q: Doesn’t this proposal lose the ability to refer to a feature by its language extension?
A: Yes, though not irrevocably. That is, under this proposal, we might imagine evolving the meaning of
-XTribbles
between language editions. Thus, talking about the “Tribbles” feature would be underspecified, as we’d have to name both “Tribbles” and the language edition. For implementors, this could be problematic, and we may to invent a term “Tribbles2024” to talk about a specific version of the Tribbles feature. But for users, the goal is for them not to worry about these details. Indeed, they might never specifyTribbles
anywhere, instead using editions and bundles to describe what kind of language they want, without sweating the details.Put another way (adopting text from Simon Marlow): 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.
Q: What’s the binary compatibility guarantee?
A: The restriction on the expressive power of language editions is that build products of different language editions must be compatible. The compatibility guarantee applies only within one release of GHC. That is, GHC 9.12 will produce binaries compatible with other binaries produced by GHC 9.12, regardless of the language edition. However, no guarantee is provided between GHC 9.12 and GHC 9.14, even under the same language edition.
Q: With the power embodied in language editions, might we be inviting a state of chaos, as these can evolve arbitrarily over time?
A: Technically, yes, we’re giving ourselves the power to make arbitrary changes from one version of GHC to the next. But this is nothing new! We’ve always had the ability to change the meaning of language extensions (or the base language) between releases. However, we have striven to make tasteful such changes. We will continue to do so.
1.9 Implementation Plan¶
This will be easy to implement; I volunteer to do so.