1 GHC2024¶
1.1 Motivation¶
The GHC20xx process proposal motivates why we feel the need for GHC20xx language editions in general.
The first edition, GHC2021, is now three years old, and I propose we define
GHC2024 now, even if turns out to be a modest change, to keep the process alive.
1.2 Proposed Change Specification¶
The GHC2024 language extensions set comprises of all language extensions
that GHC2021 comprises (official list), plus the following
1.3 Tally¶
The committee held a vote among nominated extensions with the following result. As per the GHC20xx process, quorum was ⅔, so 7 votes.
Extension |
Votes |
VZ |
AS |
SPJ |
SM |
AG |
RE |
ES |
MA |
CD |
JB |
|---|---|---|---|---|---|---|---|---|---|---|---|
10 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|
10 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|
10 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|
10 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|
9 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
||
8 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|||
8 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|||
6 |
✓ |
✓ |
✓ |
✓ |
✓ |
✓ |
|||||
4 |
✓ |
✓ |
✓ |
✓ |
|||||||
4 |
✓ |
✓ |
✓ |
✓ |
|||||||
4 |
✓ |
✓ |
✓ |
✓ |
|||||||
1 |
✓ |
✗ |
✗ |
✗ |
✗ |
NB: ImpredicativeTypes was accidentally missing on the original ballot, and some committee members may have forgotten to vote. But with explicit votes against from AG, SPJ, RE, JB it could not have made quorum.
The voting committee members are
SM |
Simon Marlow |
SPJ |
Simon Peyton-Jones |
JB |
Joachim Breitner |
RE |
Richard Eisenberg |
VZ |
Vladislav Zavialov |
ES |
Eric Seidel |
CD |
Chris Dornan |
AS |
Arnaud Spiwack |
AG |
Adam Gundry |
MA |
Moritz Angermann |
1.4 Why GHC2024 in the first place¶
There were concerns that defining GHC2024 is too soon, given that
GHC2021 has hardly reached the target audience, and that this introduces
instability.
I believe that neither are good reasons to not define GHC2024:
There is an inherent latency between defining language editions and them reaching the users. It reaches users who do not have to worry about supporting older GHC first, and thus library authors later. This is somewhat unavoidable, but not per se a reason to reduce the frequency.
Relatively frequent language editions do not introduce undue instability: Users who pin the language edition in their .cabal file or so are not affected by the existence of a new one. Neither are users who pin their GHC version. Only those users who upgrade their version of GHC _and_ ask for the latest edition may now have access to new features.
As Arnaud explained on the mailing list, Rust (generally not perceived as a language with a perception of low stability) has a very similar model with their “language editions”.
Furthermore, a regular, not too slow cadence makes the whole process more predictable, which I expect improves the perception of stability, in the sense of delivering a stable flow of changes.
1.5 Why add DataKinds and TypeData?¶
We’re considering GADTs and TypeFamilies for adoption. Both of
them greatly benefit from DataKinds and TypeData (so do
phantom types in plain Haskell 2010 for that matter). They let us
inject more types at the type level. On the other hand if we reject GADTs and
TypeFamilies from GHC2024, then we DataKinds and
TypeData may not pull their weight.
That being said, it’s been suggested
that having DataKinds on yields better error messages when
mistakenly using a data constructor in types:
foo :: Just Int
foo = Just 0
With DataKinds the error message is:
<interactive>:2:19: error: [GHC-83865]
• Expected a type, but ‘Just Int’ has kind ‘Maybe (*)’
• In the type signature: foo :: Int -> Just Int
With NoDataKinds:
<interactive>:4:19: error: [GHC-76037]
Not in scope: type constructor or class ‘Just’
Suggested fix:
Perhaps you intended to use DataKinds
to refer to the data constructor of that name?
Both DataKinds and TypeData are used the same way at the
type-level, the former lets us reuse types that we are using in
computations at the type level, whereas the latter lets us define type
purely for type level computations.
Counterarguments: TypeData is still rather new, being only
released in GHC 9.6. It’s probably too early to consider that we have
sufficient feedback on its merits. Nevertheless, it’s innocuous enough
to deserve a vote.
Backward compatibility: Enabling DataKinds or TypeData does
not affect existing GHC2021 code.
1.6 Why add DefaultSignatures?¶
There is no strong argument for inclusion of
DefaultSignatures. But nor is there for rejecting
it. DefaultTypeSignatures seems quite stable and there hasn’t been
any pushback against the feature. It’s a situational, but useful
extension that we don’t really have expectations to change.
Counterarguments: It’s been pointed out
that the most common use of default type signatures is with generic
deriving and DeriveAnyClass (which is not considered for inclusion
in GHC2024 because it’s seen as too error prone). An alternative,
for this use-case would be to use deriving-via with the
Generically type class. But there are other uses, and
Deriving-via doesn’t always work.
Backward compatibility: Enabling DefaultSignatures does not
affect existing GHC2021 code.
1.7 Why add DerivingStrategies?¶
Since GeneralisedNewtypeDeriving is part of GHC2021, it would make sense
to allow users to be explicit about when it is being used using
DerivingStrategies. For example:
newtype T = MkT String
deriving stock Eq
deriving newtype Show
Some users prefer this style, and there is little downside to being explicit
about the deriving strategy in use. Note that GHC2021 does not include
DerivingVia or DeriveAnyClass, so these strategies will still need the
corresponding extension to be enabled explicitly.
Backward compatibility: Enabling DerivingStrategies does not affect
existing GHC2021 code.
1.8 Why add DisambiguateRecordFields?¶
Suppose there are two record fields from different data types in scope, and they
have the same name. When the field is used in record construction or pattern
matching, it is easy to resolve which datatype is meant using the name of the
data constructor, but Haskell2010 and GHC2021 do not do so. For
example, the following is rejected:
module M where
data S = MkS { x :: Int }
module N where
data T = MkT { x :: Int }
module P where
import M
import N
t = MkS { x = 3 } -- ambiguous name resolution error for "x"
For a long time, GHC has supported the DisambiguateRecordFields extension, which makes use of the constructor name to allow this program to be accepted. This is a small quality-of-life improvement for users, who may otherwise see this error and not immediately understand why GHC does not make the “obvious” choice.
This is a simple extension, affecting name resolution only, without involving any type-directed disambiguation. It does not allow the definition of clashing field names in a single module, but makes it easier to avoid unnecessary errors when importing two modules that happen to use the same field name in different records.
Backward compatibility: Enabling DisambiguateRecordFields does not affect
existing GHC2021 code.
1.9 Why add ExplicitNamespaces?¶
As discussed in issue #551, GHC2021
includes TypeOperators, but does not include ExplicitNamespaces. This was
a very strange (and probably inadvertent) decision, given that the flag
-XTypeOperators enables both the TypeOperators and the
ExplicitNamespaces language extension.
It seems to be a bad idea to retroactive change GHC2021 to fix this (we
do care about stability after all), but we should certainly fix this in the
upcoming edition.
And – at least if one accepts that regular releases of GHC20xx are a Good
Thing™, adding ExplicitNamespaces alone should be sufficient to cut a new
release.
A counter-argument to ExplicitNamespaces is that it has seen changes
recently, e.g. in #281 and
#581.
Thus it may not yet be as stable as we want for GHC20xx. To keep GHC20xx
stable we could amend #281 to ask for a new extension name for syntax added there.
Backward compatibility: Enabling ExplicitNamespaces does not affect
existing GHC2021 code.
1.10 Why add GADTs and MonoLocalBinds?¶
GHC2021 includes both GADTSyntax and ExistentialQuantification, but
does not include GADTs or MonoLocalBinds. Moreover, the combination of
GADTSyntax and ExistentialQuantification is enough to define GADTs and
pattern match on them (see GHC issue #21102 for detailed discussion).
GHC 9.4 and later permits pattern-matching on an imported GADT regardless of
which extensions are enabled, but doing so will emit a warning from
-Wgadt-mono-local-binds if MonoLocalBinds is disabled. This is
consistent with the principle that extensions are required at definition sites
but not use sites. (GHC 9.2 and previous versions required GADTs or
TypeFamilies to be enabled in order to pattern match on a GADT.)
Enabling MonoLocalBinds is considered necessary for robust type inference
when pattern matching on GADTs (see section 4.2 of OutsideIn(X): Modular type
inference with local assumptions).
Moreover, writing type signatures for polymorphic local bindings generally makes
it easier to understand the code. However, the type signature requirement makes
it more difficult to factor out repeated code into a where clause (e.g. see
GHC issue #19396), and
this can surprise users and cause backwards incompatibility.
Since ExistentialQuantification allows defining types with contexts that
include equality constraints, there is not really a principled distinction
between ExistentialQuantification and GADTs. (While there is a
syntactic distinction between GADT syntax and “traditional” datatype syntax,
both forms are capable of expressing simple ADTs, existentially quantified
types, and GADTs.)
Possible ways to resolve this infelicity include:
Add
GADTsandMonoLocalBindstoGHC2024. This makes it clear that GADTs/existentials are a core part of the language, and makes the type inference compromises necessary to accommodate them. Migration advice forGHC2024should make clear that type signatures may need to be added for local bindings (orNoMonoLocalBindsspecified explicitly). Given thatMonoLocalBindsis a simpler design which can safely be extended withGADTs, it makes sense to have it be part of the base language; users can then opt-in explicitly toNoMonoLocalBindsas an extension if required.Add
GADTsbut notMonoLocalBinds. This is mostly consistent withGHC2021, but means that type inference for local bindings may not be predictable when using GADTs. Moreover, including an extension but not the extensions it implies is itself confusing (as withTypeOperators/ExplicitNamespacesinGHC2021).Remove
ExistentialQuantificationfromGHC2024. This means existentials/GADTs are clearly treated as an extension, albeit an extension that makes type inference “worse”. Users will need to understand the impact ofMonoLocalBindsonly if they import a GADT or define one after enablingGADTsexplicitly. Migration advice forGHC2024should make clear that users may need to enableGADTsexplicitly (and possibly give type signatures for local bindings or specifyNoMonoLocalBinds).
Backward compatibility: Enabling GADTs alone does not break existing GHC2021 code
(because it is equivalent to the current situation),
but enabling MonoLocalBinds does.
1.11 Why add ImpredicativeTypes?¶
The current design seems to work well for many use-cases and is unlikely to change. It’s been around since GHC 9.2. Besides, some form of impredicativity seems to be intuitively expected by many programmers. The time seems ripe.
1.12 Why add LambdaCase?¶
The latest State of Haskell 2021 Survey results list
LambdaCase as the top answer to “Which extension would you want to be on by
default”. It also missed GHC2021 by just two votes. There is a whole style
of writing Haskell that makes extensive use of \case. And (unlike the runner up in the survey, OverloadedStrings), it only enables new syntax, i.e. it does not change existing code.
A counter-agument to adding LambdaCase is that just extended the meaning of
LambdaCase with \cases in #302, and if one only
wants to add extensions to GHC20xx that have been proven to be stable, then
this one probably isn’t yet.
Backward compatibility: Enabling LambdaCase does not affect
existing GHC2021 code, with the exception of lambda-bound variable names
cases (GHC already forbids the \case even without -XLambdaCase).
1.13 Why add RoleAnnotations?¶
Roles are an essential part of modern GHC Haskell.
Role annotations are required for correctly writing types with internal invariants like Set or “fast” implementations like data Fin (n :: Nat) = UnsafeFin Int.
As GeneralisedNewtypeDeriving is in the GHC2021 language set, so should be RoleAnnotations. They are different sides of the same feature: without correct role annotations GND cannot be used safely.
At the moment, using GHC2021 together with Safe causes a warning, because Safe Haskell regards GeneralisedNewtypeDeriving as unsafe (see #19605 for discussion of this issue). A plausible way to resolve this would be to regard GeneralisedNewtypeDeriving as safe, but that assumes library authors are aware of the need for correct role annotations and insert them as needed.
Backward compatibility: Enabling RoleAnnotations does not affect
existing GHC2021 code.
1.14 Why add TypeFamilies?¶
Type families are one of the most used features of GHC. The reason for
not including TypeFamilies in GHC2021 was that type families
don’t work so well without MonoLocalBinds, and it was considered at
the time that MonoLocalBinds was too steep a change.
But if we add MonoLocalBinds to GHC2024, there is no
obstacle to make this very popular feature.
Counterarguments: A reason not to include
<https://github.com/ghc-proposals/ghc-proposals/pull/613#issuecomment-1759556663>
that the semantics of type families (in particular the strictness of
its evaluation) is unsatisfactory and would like it to change before
they become a default. But there are two possibility: either the
semantic change is backward compatible, in which case including
TypeFamilies in GHC2024 won’t cause any issue; or the semantic
change isn’t backward compatible, in which case the massive popularity
of type families makes it impossible to incorporate the change in the
TypeFamilies extension, and GHC2024 is safe.
Backward compatibility: Assuming that MonoLocalBinds is enabled,
enabling TypeFamilies doesn’t affect existing GHC2021 code
further.
1.15 Why add BlockArguments?¶
BlockArguments denoises common idioms, e.g. when for forM in do blocks, by not requiring parentheses or $. The resulting non-verbose syntax is arguably very Haskelly, and some people working with languages that have this already (Agda, PureScript, Lean) report beeing happy about it.
Backward compatibility: No incompatibilities (accoridng to the original proposal).
1.16 Alternatives¶
We could not do GHC2024 and wait yet another year, or more, because we shy away from
making what may look like a stability-threatening change.
In my view that is worse: The fixes and improvements suggested above would reach our users later, we would not establish a regular and predictable pattern, and in the worst case never dare to make a new release, which would make the GHC20xx idea fall into a similar pattern than the Haskell20xx report process, which at the moment is stalled.
1.17 Implementation Plan¶
(None yet)