GHC2024 – community input

I very much despise (though maybe that word’s a bit too harsh) the do “as a comma”.
To me (even with syntax highlighting) it just feels like another argument in the list:

myFunc a b =
    doSomething otherVal a do theRest with b
                              andThen some indentedSection

This is just a string of text, compare:

myFunc a b =
    doSomething otherVal a $ do theRest with b
                                andThen some indentedSection

Now I know it’s often done with a dangling do, but it still just feels like an argument to the preceding function, instead of a clear (non-text) separation of “text sections” (which are functions and their arguments)


At least, this is my reason to not like BlockArguments. That and I remember seeing/reading something about weird indenting interactions that make me not want to use it. (in some edge cases, I believe)

4 Likes

I assume you don’t have the same difficulty with:

myFunc a b =
    if doSomething otherVal a then theRest with b
                              else some more

If not, do you have a guess as to why keywords then and do trigger different brain circuits?

3 Likes

Can we also assume you, @rhendric, don’t have any difficulty with:

myFunc a b =                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         if
       doSomething otherVal a then theRest with b
                              else some more

But if you do, could you guess why different circuits in your brain are being activated?

When looking at that code, my process appears to be to single out then and else as keywords that divide up the remainder of the body of the function, and then to object that then does not appear to be preceded by an if clause. So, uh, yeah, I guess that’s not having difficulty? I don’t know what point you’re trying to make.

I do hate putting the then after the if's expression, in fact.
I prefer using guards whenever possible, and IF I have to use an if-then-else, I want the then on the next line

myFunc a b =
    if doSomething otherVal a
        then theRest with b
        else some more

But doing that with do-blocks is kind of weird since I’m used to the dangling do (and annoying because it introduces 3 space indenting) :

myFunc a b =
    doSomething otherVal a
        do theRest with b
           andThen some indentedSection
2 Likes

The point is context.

Before do was allowed to drift wander float, it also preceded monadic expressions. This was something you just got acquainted with (i.e. it may very have activated the same brain circuit which recognises other “leading keywords” like module , import, export, data, newtype, let and case).

So to then see do now being used in this other way is anomalous, much like this probably is:

anotherFunc a b c d fee fi fo fum =
    checkThat d fee if fo fum doSomething otherVal a c then theRest with b fi
                                                       else some more

…if not for you, then perhaps someone who’s begun to learn to use Haskell.

People have explained here the downsides of including OverloadedStrings. Are there any downsides to RecordWildCards?

1 Like

It makes large codebases unreadable.

4 Likes

It introduces symbols into scope that can not be traced back to where they come into scope without already knowing the fields of the data type they come from.
I.e. someone reading code that uses RecordWildCards will have a harder time figuring out which values in scope come from the record wild card and which come from global imports, for example.

7 Likes

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