Anybody using GHC2021

I didn’t realize that. I use stack which seems to leave it to cabal default.

I agree, it’s a pain when you have to enable it to make something just work.

TypeAbstractions looks interesting indeed.

Good to hear that, as that is the motivation for the exercise: Remove the anxiety and mental overhead of deciding whether a certain feature is “normal part of contemporary Haskell”. It doesn’t mean everyone has to use them, or that we can’t have opt-in warnings or linters that help you stick to a particular subset if you want to (to address AntC’s concerens), but otherwise they are no more “special”.

Me too! I think LambdaCase didn’t make it because there was discussion around the n-ary \cases. Similar for ViewPatters where an arguably “better” design has been floating around for a long while, although so far no one has picked it up and brought it to life.

Personally, I hope we will get GHC2024 with a few additions (I argued unsuccessfully for having 2023), also to show that this is really a continuous process, with regular updates, that new editions are nothing to be afraid of, and that code declared to use GHC2021 doesn’t break because of a new release.

9 Likes

Is there an ongoing discussion about this?

It seems silly but “anxiety” is the exact word :+1:

2 Likes

You were expecting it or you find it ugly (or both :-)?

I will kick it off in the fall, like last year.

Expecting :-). I am not using it myself, but I find the foo a b = \case … idiom rather nice sometimes. And inline \case even more. Maybe partly influenced from Ocaml’s function.

3 Likes

I also really like all the deriving ones.

1 Like

I use it, I love it, its only shortcoming is that it doesn’t include enough stuff, but the next extension will certainly fix this :wink:

11 Likes

With Stack, Hpack defaults to default-language: Haskell2010, which is why that ends up in Cabal files. It can be overridden by adding language: GHC2021 to the package.yaml and, in fact, the Stack project itself moved to GHC2021 a while ago.

In the case of Stack’s code base, the remaining LANGUAGE pragmas that get repeated at the top of many modules are: OverloadedStrings, RecordWildCards, LambdaCase, ViewPatterns, DataKinds and TypeFamilies.

6 Likes

Thanks for the clarification.

We use it at work on all libraries/executables. Haven’t really looked back since turning it on as it reduces the number of LANGUAGE pragmas we need to write (as we don’t use default-extensions in Cabal). It’s generally been pain free except:

# We disable all extensions by default, as a bunch are implied by using GHC2021. For any other
# extension that needs to be explicitly enabled, add the extension below.
- extensions:
  - default: false
  - name:
      - AllowAmbiguousTypes
      - ApplicativeDo
      - BlockArguments
      - CPP
      - DataKinds
      - DefaultSignatures
      - DeriveAnyClass
      - DerivingStrategies
      - DerivingVia
      - DisambiguateRecordFields
      - DuplicateRecordFields
      - ExplicitNamespaces
      - FieldSelectors
      - FunctionalDependencies
      - GADTs
      - ImplicitParams
      - ImpredicativeTypes
      - IncoherentInstances
      - LambdaCase
      - LexicalNegation
      - MagicHash
      - MonadComprehensions
      - MonoLocalBinds
      - MultiWayIf
      - NegativeLiterals
      - NoFieldSelectors
      - NoImplicitPrelude
      - NoMonomorphismRestriction
      - NoStarIsType
      - OverloadedLabels
      - OverloadedLists
      - OverloadedRecordDot
      - OverloadedStrings
      - PackageImports
      - PartialTypeSignatures
      - PatternSynonyms
      - QualifiedDo
      - QuantifiedConstraints
      - QuasiQuotes
      - RebindableSyntax
      - RecordWildCards
      - RecursiveDo
      - StrictData
      - TemplateHaskell
      - TemplateHaskellQuotes
      - TypeFamilies
      - UndecidableInstances
      - UndecidableSuperClasses
      - ViewPatterns
7 Likes

I’m using it a lot, at least for new code, and think it’s a very pleasant default in practice. It doesn’t enable all the extensions I frequently use, but the remaining ones I’m usually quite happy to enable selectively.

10 Likes

For the record, and/or for the planning GHC2024, there’s one particular extension in GHC2021 that adds to my anxiety and mental overhead [**]. (And a couple others that I find annoying but would put up with.) @jaror and @maxigit upthread are also lukewarm:

The ‘it’ is ScopedTypeVariables. If I count type abstractions, that’s three better ways we could achieve the same thing: ResultTypeSignatures is an accepted proposal, work has started but seems to be stalled. (§7.14.3 at that link which shows it used to be in GHC but got taken out.) I’d use GHC202n if it included ResultTypeSignatures but not ScopedTypeVariables. And there’s an accepted not implemented proposal (? at least I think not/I’ve lost track) to tame GHC’s over-enthusiasm for scoping. (He he Which mentions @nomeata’s own hankering after PatternSignatures.)

So if that proposal gets released, is that going to upset programs currently silently setting ScopedTypeVariables because GHC2021? I don’t know/it’s all too hard/I prefer to name extensions individually and not use ScopedTypeVariables in its current dangerous form.

[**] It’s the only place in Haskell where explicit forall changes the meaning of a program. Its effect is module-wide, so if I need to switch it on, I’d better check all my declarations for accidental scope-capture. The type sig might be textually a long way from the function body (say I have several equations with different pattern matches). The claim in the docos "without which [that is, the ‘it’] some type signatures are simply impossible to write. " I find just not true – especially for the sort of code newbies are writing/who are the ones most likely to get confounded by a non-obvious effect. I guess what’s making this especially nasty is that tutorial code samples use a, b, c for tyvars everywhere – both signatures and within-body annotations.

1 Like

I personnaly think that type variables should have been scoped without the need of any forall or extension. That is the only thing in Haskell (and probably all programming language) that is not scoped. Why ?

Do people really write code where the same name in the same function is used to represent two different things ?

9 Likes

I thought you cannot write/use GADTs in GHC2021?
It sounds like you are mistakenly ascribing the effects of GADTSyntax to ExistentialQuantification?
The language extensions GADTSyntax and ExistentialQuantification are both enabled in GHC2021, but the GADTs language extension is not enabled in GHC2021.

The distinction between generalized and ordinary algebraic data types is orthogonal to whether a data type is an existential data type. GADTSyntax only enables a different syntax and changes how pattern matching works for certain ordinary algebraic data types.

From the documentation of GADTSyntax:

Any datatype (or newtype) that can be declared in standard Haskell 98 syntax, can also be declared using GADT-style syntax. The choice is largely stylistic, but GADT-style declarations differ in one important respect: they treat class constraints on the data constructors differently. Specifically, if the constructor is given a type-class context, that context is made available by pattern matching. For example:

and

The result type of each data constructor must begin with the type constructor being defined. If the result type of all constructors has the form T a1 ... an, where a1 ... an are distinct type variables, then the data type is ordinary; otherwise is a generalised data type (Generalised Algebraic Data Types (GADTs)).

According to this the following data type is an ordinary algebraic data type, an OADT if you like.

data Foo a where
  Bar :: Ord b => a -> b -> Foo a

, whereas

data Foo a where
  Bar :: Foo Int

data Foo a b where
  Bar :: a -> Foo a a

are both generalized algebraic data types (GADTs).

To make it more confusing (but not really) the documentation also states:

Notice that GADT-style syntax generalises existential types (Existentially quantified data constructors)

I guess this is why GADTSyntax and ExistentialQuantification are easily conflated.

2 Likes

Try it:

{-# LANGUAGE GHC2021 #-}
data T a where
  T :: T Int

foo :: T a -> a
foo T = 5

You’ll find that GHC only gives a warning when you try to pattern match:

T.hs:5:5: warning: [-Wgadt-mono-local-binds]
    Pattern matching on GADTs without MonoLocalBinds is fragile.
    Suggested fix:
      Enable any of the following extensions: GADTs, TypeFamilies
  |
6 | foo T = 5
  |     ^

If you disable ExistentialQuantification:

{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE NoExistentialQuantification #-}
data T a where
  T :: T Int

foo :: T a -> a
foo T = 5

Then it gives a real error:

T.hs:4:3: error:
    • Data constructor ‘T’ has existential type variables, a context, or a specialised result type
        T :: T Int
        (Enable ExistentialQuantification or GADTs to allow this)
    • In the definition of data constructor ‘T’
      In the data type declaration for ‘T’
  |
4 |   T :: T Int
  |   ^^^^^^^^^^

I think I’ve heard about this from one of @rae’s videos for Tweag, but I don’t remember which one.

4 Likes

Note that this is actually new behavior in GHC 9.4: In 9.2, you get an actual error instead of a warning when trying to pattern match on a GADT with GHC2021:

Test.hs:7:5: error:
    • A pattern match on a GADT requires the GADTs or TypeFamilies language extension
    • In the pattern: T
      In an equation for ‘foo’: foo T = 5
  |
7 | foo T = 5
  |     ^

This was changed in !7042, see the linked #20485 for the motivation.

But it is indeed surprising that you can now use GADTs without enabling them, ie this compiles without any warnings or errors in GHC >=9.4:

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTSyntax #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# OPTIONS_GHC -Wall #-}

data T a where
  T :: T Int

foo :: T a -> a
foo T = 5

I wonder whether this is actually intended :thinking: EDIT: I commented on the issue linked above: #20485: Pattern matching on GADT pattern synonyms · Issues · Glasgow Haskell Compiler / GHC · GitLab

EDIT 2: I got pointed to this upstream ticket discussing whether it should be changed that one can define arbitrary GADTs without GADTs: #21102: GADT accepted without -XGADTs · Issues · Glasgow Haskell Compiler / GHC · GitLab

6 Likes

Yes, enabling GADTSyntax and ExistentialQuantification is enough to define and use GADTs. I regard it as a bug in GHC2021 that it included both but not GADTs. Unfortunately it’s not obvious of the best way to fix it since MonoLocalBinds isn’t included either, and enabling it is non-backwards-compatible.

I’m hoping we can resolve this in the next GHC20xx revision (see https://github.com/ghc-proposals/ghc-proposals/blob/51a0c9c4467f9cf3173ba06a987cce501ea242f0/proposals/0000-ghc2023.rst#why-add-gadts-and-monolocalbinds although GHC2023 didn’t happen).

2 Likes

I don’t think GADTs are stable enough to be in the next GHC202X. There have been breaking changes as recently as 9.6.1: 2.1. Version 9.6.1 — Glasgow Haskell Compiler 9.6.2 User's Guide

Edit: I guess it is just the combination of GADTs and Type Families, so perhaps the Type Families are the unstable part:

Record updates for GADTs and other existential datatypes are now fully supported.

[…]

A side-effect of this change is that GHC now rejects some record updates involving fields whose types contain type families (these record updates were previously erroneously accepted).

The next most recent breaking change was in 9.0.1:

GADT constructor types now properly adhere to The forall-or-nothing rule.

2.1. Version 9.0.1 — Glasgow Haskell Compiler 9.0.2 User's Guide, which is also not that long ago yet.

2 Likes

If it had been up to me, I might have included GADTSyntax but not ExistentialTypes/GADTs in GHC2021. But given that the de facto situation is that GADTs are supported in GHC2021, I would be tempted to continue to include them, because it would send a misleading signal to remove them now. I don’t think inclusion in GHC20XX should mean that the extension is necessarily completely stable.