Anybody using GHC2021

I’ve just discover the GHC2021, which set a few more extension than I normally do.

Is anyone using it, or would recommend against it ?

6 Likes

My very personal opinion is it sets some extensions I wouldn’t touch with a barge-pole. I’d rather leave them unset, then get the compiler to yell at me if some unfamiliar piece of code uses them, so I can eyeball that code. (The most insidious are extensions which don’t enable different syntax, but change the meaning of syntax that’s been familiar since ~2006.)

So no, I don’t set it. OTOH I’ve always been happy enough with a long list of extensions at the top of a module, so that doesn’t seem a burden.

You’re likely to find many libraries/sample code/code discussions expect those settings without making explicit mention. Indeed even if they don’t actually need all of them.

Are there any particular extensions you were surprised to see? Is it that you didn’t know of those/don’t feel a need to use them; or that you’d rather avoid them?

1 Like

It’s enabled by default if you don’t set it (but cabal init still defaults to Haskell2010). I personally like many of the extensions. The only two extensions I find annoying sometimes are PolyKinds and ExistentialQuantification. The former causes errors due to ambiguity where things would just default to Type before, and the latter means you can define (and, since GHC 9.4, pattern match on) GADTs without actually enabling the GADTs extension.

But it does have many great extensions: all the deriving extensions, Flexible*, BangPatterns, TypeApplications, StandaloneKindSignatures, NumericUnderscores.

I’m also relatively happy it includes ScopedTypeVariables because you have to use it to write some programs, but I don’t like that it means namespaces in signatures and term definitions are shared. I’d rather just use type abstractions on the term level.

6 Likes

All in all, I’m pretty happy with all of them. There of course the ones I never heard of like ConstrainedClassMethod or RelaxedPolyRec, the ones I never have the opportunity likee GADTSyntax . The one I definitely use TupleSections, NamedWildCards.
The most interesting one are the ones I don’t use yet but will use them if they are enabled like ImportQualifiedPost or NumericUnderscore and even ScopedTypedVarialbles etc … I don’t use not because I can’t be bothered but more because it feels like I am not writting orthodox code. If they are part of GHC 2021 then it becomes official and so orthodox.
For the same reason, I was expecting LambdaCase (which I don’t use, because I find it ugly, but would were enabled by default), BlockArguments (which I have been waiting for 20 years …) and possibly PatternSynonyms and 'ViewPatters`.

2 Likes

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