GHC2024 – community input

Having been following this conversation, I wonder if we can at least identify the following extensions as being uncontroversial?

  • LambdaCase
  • DerivingStrategies
  • DerivingVia

Also, following this line of thought, it might perhaps be a good idea to make a survey where people can vote both for and against language extensions… in fact, now that I think of it, @taylorfausak did exactly that in the 2022 State of Haskell Survey. Interestingly, I note that OverloadedStrings seemed a lot more popular there than it has been here — I wonder why?

6 Likes

On OverloadedStrings and ‘it breaks existing code heavily’, is there anything that can be done to get a sense of that as a matter of practice (as opposed to as a matter of principle)?

As an experiment, I set OverloadedStrings as default for Stack (default-extensions), deleted my snapshot database (so all of the 176 direct and indirect dependencies would be rebuilt), and rebuilt Stack. Nothing complained. (EDIT: Actually, rebuilding the dependencies may have been pointless; I think the OverloadedStrings default may not affect the dependencies.)

I wondered if, say, one could take the set of 3,000+ package versions in the latest Stackage LTS snapshot (or, perhaps, some older snapshot - if the past is more relevant that the current) and attempt to build them with it as a default, that might provide some intelligence about the extent of code breakage in practice. (EDIT: I see that the effect on packages in Stackage was a consideration when ‘Foldable Traversable in Prelude’ was being considered: Foldable Traversable In Prelude - HaskellWiki).

2 Likes

^ That, and also the fact that it is unsafe unless everybody uses role annotations correctly.

Oh dear. I completely missed your message there!

At this rate, I wonder if any extensions are suitable for GHC2024. If we get it at all, I imagine we’ll have to make compromises somewhere along the line — some people will be unhappy, but that’s how it is.

Isn’t it coerce that’s potentially unsafe, rather than DerivingVia that just generates code that uses coerce, and could have been written by hand?

1 Like

Yes, I just think it is not a good idea to add even more ways to trigger that unsafety to the default language. As I said:

I wouldn’t want to encourage everyone to start using coerce everywhere and so I don’t want to encourage everyone to use DerivingVia everywhere either.

But I think there is a relatively easy fix: declare once and for all that Data.Coerce is safe (and perhaps check Hackage for unsafe existing code). If we can collectively make that decision (and we improve the error messages) then I think it is fine to add DerivingVia.

The desire to consider Data.Coerce to be Safe is also the motivation for including RoleAnnotations in GHC2024, to indicate that these days, if you want to define an abstract data type, you really have to think about roles as well.

4 Likes

My vote, unavailable in the poll, is this: require GHC2024, whatever it implies, to be specified explicitly. If there’s no {-# LANGUAGE GHCxxxx #-} pragma then the default should be {-# LANGUAGE Haskell2010 #-}.

First, thanks for doing this - glad it’s happening.

At the same time I feel sad that I am not even using GHC2021 yet… Specially for libraries I think it’s hard because of the long tail of ghc versions.

2 Likes

I, for one, am happy GHC2021 exists so we can use it in our production codebase. Adding a few more syntactic sugar pragmas and other pragmas with explicit “opt-in” syntax would also be appreciated :slight_smile:

(e.g. DerivingStrategies, that you don’t use unless you add keywords in specific places)

3 Likes

If there’s no {-# LANGUAGE GHCxxxx #-} pragma then the default should be {-# LANGUAGE Haskell2010 #-} .

That’s wouldn’t make sense since GHC2021 is already the default language. See the user’s guide:

GHC2021 is used by GHC if neither Haskell98 nor Haskell2010 is turned on explicitly.

I’m aware of that. I also think it was a mistake. It may be too late to fix the mistake now, but if not, the time to do it is when the new default is set.

1 Like

Well, the problem is that you do not break the line after do. Every do should be at the end of a line, never in between.

(I posted this as a reply to @Vlix, why does it show context-less in the main stream here??)

1 Like

It is by design.

Back on topic: Haskell is becoming and will become a relatively large language. It is convenient and most likely the right path, but I am happy to have hopped in when everything was easier to grasp, because there were less things to learn, and now and then a blog-post popped up with a new extension tutorial.

I mentioned that normally you’d have a dangling do (i.e. putting a newline after the do), but used a run-on line to exaggerate my point.

funcName =
    -- otherFunc :: a -> m b -> _
    otherFunc arg1 do
        someOther arg2
        moreThings toDo

This, to me at a cursory glance, looks the same as the following:

funcName =
    -- otherFunc :: a -> b -> c -> d -> e -> f -> _
    otherFunc arg1 arg2
        someOther args
        moreThings toDo

So I’d just like a non-alphanumeric separator showing that it’s not just a bunch of arguments, so either the $ operator, or (...) parentheses to enclose the part. But I understand some people don’t have this issue, which is fine, but I just don’t like BlockArguments :man_shrugging:


P.S. And an example from this section from Typeclasses.com shows something I also dislike because it takes too long (for me) to understand the parsing order when seeing this at a glance:

{-# LANGUAGE BlockArguments #-}

import Control.Concurrent.Async (concurrently_)

blockStack =
  concurrently_
  do
    putStrLn ['a'..'z']
  do
    putStrLn ['A'..'Z']

The committee has voted, and (as things stand right now) GHC2024 will be GHC2021 plus

  • DataKinds
  • DerivingStrategies
  • DisambiguateRecordFields
  • ExplicitNamespaces
  • GADTs (and the implied MonoLocalBinds)
  • LambdaCase
  • RoleAnnotations

You can see the full tally at https://github.com/ghc-proposals/ghc-proposals/blob/joachim/ghc2024/proposals/0000-ghc2024.rst#2proposed-change-specification

13 Likes

Isn’t GADTs obsolete in light of ExistentialQuantification and TypeOperators, which are already enabled by GHC2021?

1 Like

The discussion here may assist: https://github.com/ghc-proposals/ghc-proposals/blob/joachim/ghc2024/proposals/0000-ghc2024.rst#10why-add-gadts-and-monolocalbinds

2 Likes

Honestly I wish RecordWildCards were banned and completely removed from GHC, RecordDotSyntax is strictly better in every aspects. I can understand why people felt the need for it at times but this should just be history now, it’s a terrible terrible extension.

4 Likes

Is ‘should now be history’ premature? RecordWildCards has been supported from GHC 6.8.1 but OverloadedRecordDot and OverloadedRecordUpdate are only supported from GHC 9.2.1 and the latter comes with an ‘EXPERIMENTAL’ warning in the documentation for GHC 9.2.1 to GHC 9.8.1.

Am I right to understand that, to get its full syntax benefits, moving from RecordWildCards to record dot syntax involves, in practice, the renaming of fields to remove the prefixes that identify the type? Taking my Stack code base example:

Step 1: just use a dot syntax:

-- | Interprets BuildOptsMonoid options.
buildOptsFromMonoid :: BuildOptsMonoid -> BuildOpts
buildOptsFromMonoid buildMonoid = BuildOpts
  { boptsLibProfile = fromFirstFalse
      (buildMonoid.buildMonoidLibProfile <>
       FirstFalse (if tracing || profiling then Just True else Nothing))
  ...
  -- 26 other fields relying on the 31 fields of a `BuildOptsMonoid` value
  ...
  , boptsDdumpDir = getFirst buildMonoid.buildMonoidDdumpDir
  }
 where
  ...

Step 2: also eliminate the use of prefixes in field names to identify the type:

-- | Interprets BuildOptsMonoid options.
buildOptsFromMonoid :: BuildOptsMonoid -> BuildOpts
buildOptsFromMonoid buildMonoid = BuildOpts
  { libProfile = fromFirstFalse
      (buildMonoid.libProfile <>
       FirstFalse (if tracing || profiling then Just True else Nothing))
  ...
  -- 26 other fields relying on the 31 fields of a `BuildOptsMonoid` value
  ...
  , ddumpDir = getFirst buildMonoid.ddumpDir
  }
 where
  ...

Currently, there are 118 instances of {..} in the Stack project: 39 in the pantry library and 79 in Stack itself. I am happy to work through eliminating all of them but I read the guidance in the GHC documention as saying, in terms, ‘hold off from doing that - things may change’.

1 Like