
On 25/01/2024 06:14, Moritz Angermann wrote:
Maybe I'm misreading this, but this can potentially break existing code, can it not?
I don't think this has been released yet, because RequiredTypeArguments is coming as part of GHC 9.10, and the fork is due in about a month. So there's no existing code to break and we can freely choose at this point, provided the decision is made in good time. Of course if 9.10 ships with option 1 and we later switch to option 2, that would be a breaking change. But it looks like HEAD might currently have option 2 implemented anyway? As to the proposal overall, I'm rather on the fence. Adam
On Thu, 25 Jan 2024 at 02:47, Richard Eisenberg
mailto:rae@richarde.dev> wrote: Proposal #606 https://github.com/ghc-proposals/ghc-proposals/pull/606 has been submitted to the committee, by Vlad.
This proposal makes a tiny change. Suppose we have `f :: forall (x :: Type) -> x -> ...` (this is part of -XRequiredTypeArguments). Then (as proposed) we can write `y = f (type Int -> Int) abs ...`. The `type` keyword here is to signal that what comes is a type: it should be parsed as a type and name-resolved as a type. This might matter if there is, say, a constructor Int in scope. The proposal at hand is whether to accept the above program or to require extra parentheses, thusly: `y = f (type (Int -> Int)) abs ...`. That's it -- that's the entire proposal. (It says we should _not_ accept the former, without parentheses.)
The motivation is to avoid surprising users. Normally when we have `word1 word2 -> word3`, word1 and word2 will associate more closely than the arrow. Yet the first example has `type Int -> Int` where the arrow binds more tightly. Because `type` is a keyword, this is no challenge to parse and is not ambiguous -- it's just perhaps confusing to users.
There was some debate in the proposal, but in my perusal, not a clear indication toward any particular direction. The most rigorous statement I could find is that the new syntax is a subset of the original, and so we can easily reverse this decision later.
I propose we vote on the matter. We have two choices:
1. Original syntax: allow `(type Int -> Int)` as an argument. 2. Amended syntax: require `(type (Int -> Int))` as an argument.
--------------------------------
My vote: 1.
There are many keywords in Haskell, and we are used to parsing these differently. For example, if we have `f (do x <- blah ...)`, we know quite well that the <- is within the `do`, not the other way around. Ditto `case`: we don't require scrutinees to be parenthesized. I posit that the strangeness some have felt around `(type Int -> Int)` is (understandable) confusion in the face of novelty. But the `type` herald will not be novel forever, and I think we'll enjoy having fewer parentheses in our code in the long run. (I might be arguing for "2 today, then 1 tomorrow". But let's just skip the intermediate step and do 1 now.)
I welcome your opinions and votes. It would be great to conclude this in the next 2 weeks, by Feb 7.
Thanks! Richard
-- Adam Gundry, Haskell Consultant Well-Typed LLP, https://www.well-typed.com/ Registered in England & Wales, OC335890 27 Old Gloucester Street, London WC1N 3AX, England