Which extensions should be part of the next GHC20xx?

Thanks for the clarity!

Understood, any reasons for this ?

Historically, defaulting is different as well. We have defaulting rules for overloaded numbers, but not for strings. I don’t know if the new exportable default feature has been implemented, if it has, we could make OverloadedString together with a new defaulting rule to String in Prelude.


As I’ve been rather public about in the past, I would like to advertise for a very inclusive list of extension for the next edition. In my opinion, we should think of extensions as included by default, and exclude them if we have a reason to.

In this spirit here’s the (raw) list of all the extensions which aren’t in GHC2024

  • AllowAmbiguousTypes
  • AlternativeLayoutRule
  • AlternativeLayoutRuleTransitional
  • Arrows
  • AutoDeriveTypeable
  • CApiFFI
  • CPP
  • CUSKs
  • DatatypeContexts
  • DefaultSignatures
  • DeriveAnyClass
  • DerivingVia
  • BlockArguments
  • DoRec
  • DuplicateRecordFields
  • ExtendedDefaultRules
  • ExtendedLiterals
  • FunctionalDependencies
  • GHCForeignImportPrim
  • GeneralizedNewtypeDeriving
  • ImplicitParams
  • ImpredicativeTypes
  • IncoherentInstances
  • TypeFamilyDependencies
  • ApplicativeDo
  • InterruptibleFFI
  • JavaScriptFFI
  • LexicalNegation
  • LiberalTypeSynonyms
  • LinearTypes
  • ListTuplePuns
  • MagicHash
  • MonadComprehensions
  • DeepSubsumption
  • MultiWayIf
  • NPlusKPatterns
  • NegativeLiterals
  • NullaryTypeClasses
  • NumDecimals
  • OverlappingInstances
  • OverloadedLabels
  • OverloadedLists
  • OverloadedStrings
  • PackageImports
  • ParallelArrays
  • ParallelListComp
  • PartialTypeSignatures
  • PatternSignatures
  • PatternSynonyms
  • PolymorphicComponents
  • QuantifiedConstraints
  • QuasiQuotes
  • QualifiedDo
  • Rank2Types
  • RebindableSyntax
  • OverloadedRecordDot
  • OverloadedRecordUpdate
  • RecordPuns
  • RecordWildCards
  • RecursiveDo
  • RelaxedLayout
  • RequiredTypeArguments
  • StaticPointers
  • Strict
  • StrictData
  • TemplateHaskell
  • TemplateHaskellQuotes
  • TransformListComp
  • TypeAbstractions
  • TypeData
  • TypeInType
  • TypeFamilies
  • UnboxedTuples
  • UnboxedSums
  • UndecidableInstances
  • UndecidableSuperClasses
  • UnicodeSyntax
  • UnliftedDatatypes
  • UnliftedFFITypes
  • UnliftedNewtypes
  • ViewPatterns

ETA the extensions added to GHC 9.12 and 9.14:

  • NamedDefaults
  • OrPatterns
  • MultilineStrings
  • ExplicitLevelImports
  • ImplicitStagePersistence

I was wondering if it would be possible to change the way language editions work for GHC2027.
Currently language editions are defined as sets of language extensions, and it is possible to disable individual extensions that are a part of it. For example, BinaryLiterals is a part of GHC2021, but it is possible to disable it, and the following code does not compile:

{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE NoBinaryLiterals #-}

main = print 0b1010

I think there might be a case to strengthen the definition of a language edition to disallow that. For example, it would make sense to say that if you choose GHC2027 you opt into NoStarIsType, NoDatatypeContexts and NoListTuplePuns (just as an illustrative example, not a concrete suggestion). This would allow it to guard some expressive dependent Haskell extensions by a language edition: I.e. if you want to use AdvancedDHFeature then you need to use the language edition GHC2027 which cannot be combined with StarIsType.

2 Likes

The same could be achieved if AdvancedDHFeature implies NoStarIsType, i.e., no need to change how language editions work. This would reuse the existing idiom of extension implication. (Although, to be fair, I don’t know if an implied negative is used anywhere currently.)

If you want to forbid all overrides of the extensions in a language edition that could be very cumbersome for cases where one wants to only slightly deviate from it. If you want to only forbid certain overrides in certain language editions, that sounds like it would be very complex, at least to document and understand.

2 Likes

I would like to see NumDecimals added, which I feel is still underused due to its misleading name and the fact that it used to be quite poorly documented. IMO it has no practical downside (less than OverloadedStrings for sure) and it plays nice with HexFloatLiterals which is already in since GHC2021.

4 Likes

@DavidB In principle it seems reasonable that GHC might reject some combination of language extension settings as being unsupported. I don’t think we want to make it a general rule that you can’t toggle extensions on/off within a language edition though, as that is generally less flexible and can restrict incrementally migrating to the edition (e.g. imagine if most of your large codebase compiles with GHC2021 but a few modules need NoBinaryLiterals to fix parse failures).

AFAIK there isn’t really a principled difference between turning on a disabled-by-default extension and turning off an enabled-by-default extension. For example, MonoLocalBinds is phrased as something you can “add” to Haskell2010, but it could just as well be PolyLocalBinds that is included in Haskell2010 but can be “removed” (just like TraditionalRecordSyntax).

1 Like

I agree that this would make language editions less flexible, but the upside is that when you would see a language edition you would have stronger guarantees: “This project uses GHC2027 as a default language, so I won’t see any uses of StarIsType in this codebase”, for example. It would also be an incentive to tackle issues and update the codebase: “Our team wants to use GHC2027, so we should finally get rid of the remaining uses of DatatypeContexts”.

Both policies make sense. What language editions currently provide is the convenience to not list a lot of extensions individually. What they do not allow to express or communicate is that the codebase doesn’t use certain deprecated or legacy extensions.

2 Likes

Another thing to note with OverloadedStrings is that the Num hierarchy has a bunch of useful functions, where IsString only supports String -> s

One might say that’s a lot better though, because people like doing things like saying

data Lit =  
  = IntegerL Integer
   | RationalL Rational
   | StringL String
instance IsString Lit where 
  fromString = StringL

instance Num Lit can’t be defined due to the other Num functions. I thing IsString is the better designed class.

4 Likes

Yes, I agree :slight_smile:

I guess my point is more about there not being any other classes related to string-like types out-of-the-box.

In order to practice what I preach, I classified all these extensions (based on what has been said in this thread, and on what I know)

Should be excluded

With their justification

  • AllowAmbiguousTypes (worsens type errors)
  • ApplicativeDo (silently changes the semantics of programs)
  • CPP (rarely used, not worth the extra processing time, probably)
  • CUSKs (deprecated in favour of toplevel type signatures)
  • DatatypeContexts (obsolete)
  • DeepSubsumption (not recommended as it can change the semantics of programs)
  • DeriveAnyClass (unsafe, provided more safely by DerivingStrategies)
  • GeneralizedNewtypeDeriving (already provided by DerivingStrategies)
  • ImplicitParams (not currently recommended)
  • IncoherentInstances (deprecated in favour of individual pragmas)
  • LinearTypes (unfortunately still rather shaky)
  • MonadComprehensions (probably bad because of defaulting)
  • NPlusKPatterns (deprecated)
  • OverlappingInstances (deprecating in favour of individual pragmas)
  • OverloadedLists (need a defaulting story)
  • OverloadedStrings (need a defaulting story)
  • ParallelArrays (deprecated)
  • QuasiQuotes (Ambiguity with monad comprehension, maybe other averse effects due to the dependency on Template Haskell)
  • Rank2Types (subsumed by ImpredicativeTypes/RankNTypes)
  • RebindableSyntax (creates ambiguities)
  • RequiredTypeArguments (needs a migration path)
  • Strict (change the semantics)
  • StrictData (change the semantics)
  • TemplateHaskell (affects re-compilation avoidance)
  • TemplateHaskellQuotes (affects re-compilation avoidance I think)
  • TypeInType (deprecated)
  • UndecidableInstances (deactivate useful checks)
  • UndecidableSuperClasses (deactivate useful checks)
  • ExtendedLiterals (bug #27720)
  • UnboxedTuples (bug #27720)
  • UnboxedSums (bug #27720)
  • NoListTuplePuns (it forbids too much code)
  • ExplicitLevelImports (new in GHC 9.14 and significantly changes the way Template Haskell imports works)
  • NoImplicitStagePersistence (new in GHC 9.14 and significantly changes the way Template Haskell imports works)

I feel incapable of making a pronouncement on

Because I’m missing information

  • DoRec (I don’t know what this is, is it a deprecated name for RecursiveDo?)
  • DuplicateRecordFields (I don’t know what to recommend re:records)
  • OverloadedRecordDot (I don’t know what to recommend re:records)
  • OverloadedRecordUpdate (I don’t know what to recommend re:records)
  • MagicHash (Is it affected by bug #27720 as well?)
  • MultiWayIf (subsumed by \cases, but on the other hand, harmless)
  • OverloadedLabels (is it affected by bug #27720 as well?)
  • PatternSignatures (undocumented?)
  • CApiFFI (what are the consequences of these FFI extensions?)
  • GHCForeignImportPrim (what are the consequences of these FFI extensions?)
  • InterruptibleFFI (what are the consequences of these FFI extensions?)
  • JavaScriptFFI (what are the consequences of these FFI extensions?)
  • UnliftedFFITypes (what are the consequences of these FFI extensions?)
  • RecordPuns (ETA: is this an alternative name for NamedFieldPuns?)

To be included

All the other extensions.

  • Everything in GHC2024
    • Except RankNTypes since it’s subsumed by ImpredicativeTypes, I think
  • Arrows
  • BlockArguments
  • DefaultSignatures
  • DerivingVia
  • FunctionalDependencies (debated, see Which extensions should be part of the next GHC20xx? - #37 by adamgundry)
  • ImpredicativeTypes
  • LazyFieldAnnotations (from the thread, what is this?)
  • LexicalNegation (already there, maybe?)
  • LiberalTypeSynonyms
  • NegativeLiterals
  • NoStarIsType (not in my list, because it’s a No, but from the thread)
  • NullaryTypeClasses
  • NumDecimals
  • PackageImports
  • ParallelListComp
  • PartialTypeSignatures
  • PatternSynonyms
  • PolymorphicComponents
  • QualifiedDo
  • QuantifiedConstraints
  • RecordWildCards
  • RecursiveDo
  • StaticPointers
  • TransformListComp
  • TypeAbstractions
  • TypeData
  • TypeFamilies (debated, see Which extensions should be part of the next GHC20xx? - #37 by adamgundry)
  • TypeFamilyDependencies
  • UnicodeSyntax
  • UnliftedDatatypes
  • UnliftedNewtypes
  • ViewPatterns
  • NamedDefaults (new in GHC 9.12)
  • OrPatterns (new in GHC 9.12)
  • MultilineStrings (new in GHC 9.12)

Are there objections to this list (in either direction)? Please help with the undecided extensions

6 Likes

Daily reminder that IsString has sketchy instances: Surprising behavior of ByteString literals via IsString · Issue #140 · haskell/bytestring · GitHub

It’s also sketchy for Text (due to Surrogates), but that’s less relevant for OverloadedStrings.

It’s also one of those extensions that makes people stop thinking about the intricacies of strings, so I dislike it with a passion.

5 Likes

@aspiwack I see that GADTs are included in GHC2024 but type families aren’t. What was the motivation behind this choice? Before adding TypeFamilies to GHC2027 maybe we should check whether those objections are still valid.

Well, no reason that I can see. Everyone who spoke up, as far as I could find, was in favour of including TypeFamilies, but we took a rather conservative voting approach and not enough committee members voted for TypeFamilies so it ended up not included.

That’s an interesting approach @aspiwack, thanks for working through its consequences. If we’re looking at extensions as a whole it may also be interesting to look at this unofficial spreadsheet I made a while back, which I’m trying to update with more recent suggestions.

I’m less keen on taking an “include everything” approach to GHC20xx because I would like us to be able to ensure that programmers can use GHC20xx and be reasonably confident that (a) the included extensions are basically well-designed and fairly bug-free, and (b) their code won’t be broken by GHC version upgrades. In turn that means we need to restrict ourselves to extensions where the design and implementation are stable. (The process for extension stability classification is relevant here.)

Neither TypeFamilies nor FunctionalDependencies is well specified and they pull in quite a lot of typechecking complexity with weird edge cases (more so than GADTs). That means it’s hard to ensure that the constraint solver remains backwards compatible. In practice, most programs are mostly okay, but I think we should aim for a higher standard for extensions that we turn on by default.

4 Likes

I think the general solution to that might be to add a way to set language extension flags, that have precedence over all local settings in a package or unit. E.g., add a global-extensions field to cabal, that overrides default-extensions and any extensions set in a module or on the command line.

So then, if a package declares global-extensions: GHC2024 we can be sure that no module local settings contradict that.

These are certainly reasons for exclusion (I’m not advertising to include everything, just to have reasons to excludes rather than reasons to include).

Now, I personally don’t agree with these reasons: any change to the semantics of TypeFamilies or FunctionalDependencies would, in my opinion, be a bug. These are long-established largely used extensions, changing them would be a massive compatibility regression, no matter how they are officially classified.

My goal, which I’ve so far failed to state explicitly, is that writing “normal Haskell” shouldn’t require extensions beyond the language edition (I think of it as a condition for backward compatibility guarantees). And my feeling is that type families and functional dependencies are very much normal Haskell (nevertheless, let me add a note in my message).

4 Likes

Yes. I’ve never considered enabling MagicHash by default as I imagine # is quite useful for DSLs. But maybe it would make sense for consistency given -Woperator-whitespace-ext-conflict is on by default.

My thinking is that, considering that MagicHash is necessary for working with unboxed primitive types like Int#, it would be quite reasonable for it to be enabled by default (it seems to me that if we want UnboxedTuples and UnboxSums we would also want MagicHash). Even if we can’t do that quite now.