Which extensions should be part of the next GHC20xx?

  1. MultiWayIf (subsumed by \cases, but on the other hand, harmless)

I think it’s easy too easy to dismiss the impact of syntactic extension on the ability to give good and useful errors when code fails to parse.

Not only is it harder to infer user meaning if there is a wider range of valid syntax, it also makes for a more complex parser that is harder to wrangle into giving those good errors.

Personally I don’t think being able to write:

if
  | guard1 -> expr1
  ...
  | guardN -> exprN

instead of

case () of
  _ | guard1 -> expr1
  ...
  _ | guardN -> exprN

Is worth that complexity.

  1. OverloadedLabels (is it affected by bug #27720 as well?)

I’m fairly sure it does.

  1. CApiFFI (what are the consequences of these FFI extensions?)
  2. GHCForeignImportPrim (what are the consequences of these FFI extensions?)
  3. InterruptibleFFI (what are the consequences of these FFI extensions?)
  4. JavaScriptFFI (what are the consequences of these FFI extensions?)
  5. UnliftedFFITypes (what are the consequences of these FFI extensions?)

All of these essentially just allow more types of FFI. There is a weak “more footguns” argument against some of these. But a user has to opt into them and I don’t think they steal any valid syntax either.

I think the only one of those I would object to is InterruptibleFFI, @wz1000 recently discovered some issues in the implementation of interruptible FFI and it might not be possible for GHC to uphold what that extension promises in all cases. I hope he finds the time to write up the details soon.

It’s also rarely used. So we should discourage use of this extension for the time being, until it’s clear if it can be salvaged and how that would look like.

I think TypeFamilies have a big syntactic bug: they start with an uppercase letter, unless they are an operator in which case they follow the normal operator syntax (they don’t use the convention of starting operators with :). This is confusing and inconsistent. Before TypeFamilies, names that started with an uppercase letter were known to be normal forms.

I feel like we will have to reckon with this at some point in the development of Dependent Haskell.

3 Likes

I thought about that a lot and mostly agree. But I think there is a difference in quantity and quality of syntax that these extensions steal.

If we are happy to give up # as a tight infix operator for GHC20XX then enabling all of them becomes a no brainer. I’m not sure the community as a whole agrees with that though even if I would like it.

In terms of things to remove I would like to see TupleSections go away.

They conflict with allowing redundant commas at the end of tuples and often lead to less readable code. I would far prefer the ability to have redundant commas at some point in the future over having TupleSections.

3 Likes

Regarding TupleSections and redundant commas, this recent proposal is relevant:

My claim is that TupleSections is now well enough established and sufficiently popular that adding redundant commas for tuples would cause more confusion than the value it would give. But others are welcome to disagree. :smile:

5 Likes

This is an interesting question. It would be somewhat bold to do this, but I think @aspiwack has a good point that we should try to minimize the number of extensions that need to be enabled just to do “normal” things. And it is a simpler story overall to say that MagicHash is always enabled, rather than code somehow indicating whether it is “low level and wants to use MagicHash and friends” or “high level and wants to use # as an operator more easily”.

Since GHC2027 is opt-in, I do think we can afford to make a few such bold choices.

2 Likes

This isn’t a type families thing unfortunately. Everything type-level identifier follows this same rule. They are either 1/ Capitalised or any operator (type constructors/type families/type synonyms), or 2/ lower case and can’t be operators (type variables).

I understand that it wasn’t the case in early Haskell, but the ability to use type constructors like ~> was seen as important enough to give up the ability to have operator variable.

I do personally lament this decision, but it’s a bit late to go back on it. At any rate, because it’s completely regular, this has no bearing on what goes or doesn’t in GHC2027. So it’s a conversation for another thread.

2 Likes

Well, I believe the syntax is purely opt in, and there’s no effect on people who don’t want to use it. Which is why I regard it as harmless.

But since we have LambdaCase in GHC2024, we can already do

\cases
  | guard1 -> expr1

  …

  | guard2 -> expr2

The difference between this and a multiway if is small enough that we may want to deprecate multiway if altogether in favour of \cases.

8 Likes

See Lazy Field Annotations by sjakobi · Pull Request #752 · ghc-proposals/ghc-proposals · GitHub.

It’s currently scheduled for release in GHC 10.2, which makes me wonder if it might be a bit late for GHC2027 – since I don’t expect 10.2.1 to be released in 2026. It’s a very simple extension though, so maybe it doesn’t need much real-world testing to be included.

1 Like

TIL \cases can replace multiway if, although it’s not immediately obvious to me if I were to read it in the wild, as my immediate read of \case / \cases is that it creates a lambda

8 Likes

For the better :laughing:

4 Likes

It creates any number of lambdas, including zero!

(Admittedly, it’s a tad cheeky. I might never had noticed either if Jakob Brünker hadn’t remarked it in their proposal).

3 Likes

As far as i am aware LexicalNegation tried to fix parse issues that NegativeLiterals left unadressed.
GHC2024 does include neither, likely because it changes semantics of programs. I would only enable LexicalNegation, as it seems to be a superset of NegativeLiterals (it works when negating non-literals).
I can think of two cases where it changes existing programs. First is where x - 5 is written as x -5 which will likely fail compilation, so it should be fine.
The other is when abusing the fact that fromInteger does not specify it should work on negative values; in my opinion it should be named fromNatural :: Natural -> a. This will likely effect DSLs, which will fail or misbehave at runtime. I would like to see this resolved by changing the Num hierarchy to include a class for non-negative numbers.

1 Like

Related:

2 Likes

Also somewhat related: Add `fromIntegerMaybe` to `Num` for safe numeric conversions · Issue #40 · haskell/core-libraries-committee · GitHub

No! If a module declares {-# LANGUAGE Haskell2010 #-} at the top I want to be damn sure it doesn’t use any extension whatsoever, regardless of what Cabal might say.

I didn’t think thoroughly about it, but I might support a pair of Cabal fields minimal-extensions/maximal-extensions, which would make sure that every module in the package uses respectively at least/at most the listed extensions. If any module declares a set of extensions outside the range, Cabal would report an error rather than impose the module’s language extensions.

Ah, yes. Declaring for a package, what the allowed and required extensions are and throwing an error when a module violates these constraints seems like a better idea actually.

I think it would be GHC emitting the error though, right? Cabal doesn’t parse module files AFAIK.

1 Like