GHC2024 – community input

RWCs can also be dangerous, as:

  1. They shadow existing names (they are unhygienic).

  2. Their implicit nature makes it easy to forget to initialize all fields if you don’t have the right warnings on:

    data Foo = MkFoo { a :: Int, b :: String }
    
    mkFoo :: IO Foo
    mkFoo =
      a <- ...
      b <- ...
    
      -- good luck catching this if Foo's fields ever changes
      pure (MkFoo {..})
    

NamedFieldPuns are a much better way to support this style.

2 Likes

Thanks for the replies on RecordWildCards. Am I correct to understand that those are reasons not to use it, if it is available, as opposed to reasons not to have it available? (I am contrasting with OverloadedStrings, where it was explained that having it available could break some existing code.)

My interest is that the Stack code base uses it widely, in about 30 of its 183 modules. An example of use is:

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

Am I correct to understand that those are reasons not to use it, if it is available, as opposed to reasons not to have it available?

I totally agree with you. I think the argument against any extensions being part of GHCxxx (or Haskellxx) is that by accepting it, it will be there forever. By not accepting it, we keep the door open for a new extension with a better design superseded the previous one. A bit like type families was seen as making functional dependencies obsoletes and then not … so we keep both.

However, we all know that it is really unlikely that something come and make RecordWildCard obsolete (let alone removing it).

I am in the opinion that pretty much every thing which has been implemented, enhance the user experience and doesn’t break any code should be included by default.

-Wincomplete-record-update ?

Update

I don’t think it works. I am pretty sure there is a warning for that, but I can’t find it.

More update

-wmissing-field and -wunused-record-wildcard

I think that inclusion of an extension in GHC20XX should also mean that the extension is hard to abuse by those who are unfamiliar with the extension and their possible harmful consequences. Or conversely, if an extension is included then everyone should be encouraged to check it out and try to use it in their programs.

That’s why I am for extensions like RoleAnnotations which are necessary for safety in some cases, and against including things like GADTs and TypeFamilies which often make code overly complicated.

I think RecordWildCards is in a bit of a grey area. It can remove a lot of clutter when dealing with large records, but you need to be careful and enable warnings.

6 Likes

So there is, possibly, an interaction between the choice of a default set of LANGUAGE pragmas and the bundling of GHC warnings (including in project templates: Stack project templates and GHC warnings). In that regard, -Wall includes -Wincomplete-record-updates.

2 Likes

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.