New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lambda expressions with guards and multiple clauses (was: \ of
, -XMultiWayLambda)
#302
Conversation
This proposal introduces a new extension `-XLambdaGuards` that allows guards to appear in lambda expressions. [Rendered](https://github.com/JakobBruenker/ghc-proposals/blob/proposals/0000-lambda-guards.md)
+1. I don't really like guards, but as long as we have guards it's just inconsistent to not allow them here. |
There's something funny about this. We already have
This alllows multiple alterantive patterns and guards, but I think it is limited to one argument. You propose that
which allows multiple arguments, and guards, but only one set of pattern matches. Ordinary function definitions allow multiple arguments, multiple alternative patterns, and guards. There is clearly someting a bit unsatisfying about this state of affairs. I wonder about instead generalisiing
|
@simonpj Extending
|
That said, your comparison to ordinary function definitions does make me wonder whether there's a nice way to extend regular lambda syntax (without The following wouldn't work: foo
\(Just x) n -> Just n
\Nothing _ -> Nothing
EDIT: This is incorrect - I had been under this impression because I had been doing something similar with I'm just spitballing here, but maybe it could work if foo
\(Just x) n -> Just n
Nothing _ -> Nothing This would achieve parity between lambda expressions and ordinary function definitions (when combined with this proposal), but I don't know if this is the best way to accomplish that. I'd love to hear what everyone's thoughts about it are though. |
This seems to reiterate the same discussions we had when introducing |
Thank you for bringing up those links, @nomeata. Looks like I'm certainly not the first to think about that syntax for multi-clause lambdas, and it has a few issues. The to me most compelling alternative raised in the thread is to use a keyword for multi-clause lambdas, though this comes with problems of its own, most prominently which keyword to use. It would be great if we could reach consensus on a design in context of this discussion, but if not I don't think it should significantly hold up the proposal as it stands, since it has value on its own and should be, I believe, much less controversial. Edit: though in light of this I'll have to think more about the fact that lambdas aren't layout heralds, whereas multi-way ifs are. This could lead to some annoyances in the proposal as is. |
Ah if this requires layout too, I night rather just have a Besides those links above, soem of the first ghc-proposals in this repo were about these things, and my recollection was that a layout-based lambda would allow everything missing that's wanted today. The only downside was the inability to write things which the community increasingly considers bad style. |
I've thought about it a bit more, and, while I'm not very familiar with layout rules, I actually think it shouldn't be much of an issue, with the current proposal. With just that, lambdas would simply continue not heralding layouts, and only a guard in a lambda would herald a new layout. That said, in principle I'm not opposed to making a broader change and using a different extension name for it. Edit: Ah, thought about it some more again and actually it may not be that easy. The problem (if I'm understanding layouts correctly) is that it's not clear which token should be the layout herald. It can't be the
You're probably referring to this proposal: #18 The most common complaint appears to that if lambdas being layout heralds were implemented under an extension, old style monadic style wouldn't work quite as it did, so this wouldn't work: do
f a >>= \b ->
g b >>= \c ->
h c I believe even indenting it like this wouldn't work: do
f a >>= \b ->
g b >>= \c ->
h c One option would be something like do
f a >>=
\b -> g b >>=
\c -> h c Or of course it could be simply replaced with do notation, which is probably the sensible thing to do anyway: do
b <- f a
c <- g b
h c Though one commenter also mentioned that the following wouldn't work: foo = \x -> do
a x
b This would have to be replaced by one of the following options: foo =
\x -> do
a x
b
foo' = \
x -> do
a x
b or something similar. So the question is, are we okay with these examples not working with the respective extension enabled? |
(Closing was a misclick, sorry) |
Hmm, that is worse than I thought. Thanks for writing those examples. |
There is abandoned proposal about multi-argument |
Ah, I can't tell if I'm being sarcastic or not, but we could do it for explicit braces only and not layout, until the layout is figured out. I want to add a layout rule where one layout herald can be "dominated" by another, e.g. do x do
y
z But I don't know the algorithm well enough to make a concrete suggestion. |
Another option would be to only have Edit: Correction, it is not a parse error currently, so I'll have to think a bit about how it would change current behavior. |
Regarding layout for guards: For most places where guards can occur, they do not begin a layout, so the following works: a = case () of _ | True -> 0
| False -> 1
b = let foobar | True = 0
| False = 1
in foobar With x = if | False -> if | False -> 1
| False -> 2
| True -> 3 It would be intuitive for However, this does make things slightly inconsistent, since for example the following does not parse (compare with the first example): a' = if | True -> 0
| False -> 1 Lambdas (currently) are not layout heralds, so we could potentially run into the same issue as I believe that the lexer can be adjusted such that patterns in lambdas are layout heralds iff followed by a Optionally, to make it more consistent, this same rule could be applied to guards elsewhere if the extension is enabled. (Or similar rules if guards are preceded by an identifier instead of a pattern). |
Well, I feel like a fairly coherent idea is forming here.
f A S | g1 = a
| g2 = b
f _ _ | g3 = c
| g4 = d one could always write f = \
A S | g1 -> a
| g2 -> b
_ _ | g3 -> c
| g4 -> d This is (more or less) something @Ericson2314 suggested in #18 (comment), though he mentions "weird type inference issues", which might be worth looking into. These three points give lambdas parity with regular function definition and make the language simpler in the sense that the functionality of three (or four) features has been combined into one, in a way that's consistent with the existing language. I'll write this into the proposal later today. |
I had been dubious of this line of inquiry. No longer! One possible improvement (or unnecessary complication, depending on your point of view): foo (Just x)
| x < 0 = ...
| let y = blah + 1 = ...
where
blah = x + 5 Note that the With the new syntax, could we have foo = \
(Just x) | x < 0 -> ...
| let y = blah + 1 -> ...
where blah = x + magicNumber
Nothing -> magicNumber
where
magicNumber = 5 Note that my first One might reasonably argue that this indentation-awareness is too subtle. I wouldn't argue hard against. But it seems like a nice opportunity with this new syntax. I really like it. |
We already have nested And that's good, because I love this. |
@goldfirere I like that idea, I'll add it to the proposal. I was going to say that for consistency's sake, it would make sense to add that same where behavior to clauses of |
Not to lessen our enthusiasm, but this proposal does have a negative: it introduces the first place where Haskell is newline-aware. That is, with this proposal, a newline has different semantics than other whitespace. This is a new aspect of Haskell (outside of string/quasi-quote literals). Of course, newlines have interacted with indentation for eons, but these have always been equivalent: tok1 tok2 tok3 and tok1 tok2
tok3 With your proposal, that's no longer true. To me, this isn't a significant downside, but it's making the language even more whitespace-aware than previously. This relates, of course, to the fact that it's not backward compatible. \
x
y -> blah was previously acceptable, but will no longer be with this proposal (IIUC). |
One more thing: might I humbly suggest |
@goldfirere Yes, I've looked into that, and only three packages on hackage currently have a newline immediately after a backslash in a lambda, and I believe With the most straightforward specification I agree that your example wouldn't work. I was considering Edit: Ah, actually I was slightly wrong about newlines after
Still, not too bad I think, especially since this incompatibility is guarded behind an extension and it would be easy to adapt code. |
My worry isn't really about back-compat -- it's more about humans having to reprogram their brains' internal Haskell lexers. Again, this isn't a deal-breaker for me. It's just (to me) a drawback. The benefits outweigh the drawback. Sorry to shamelessly repeat @Ericson2314's suggestion instead of looking back. |
Here's a question, if we make I would rather not due newline sensitivity until later when we've exhausted other options, and per #302 (comment) I think it's a deficiency with layout in general, and not lambda in particular. |
In other words, is there an example that compiles if Yes, if you use multiple clauses: \(Just x) -> 0
Nothing -> 1 So no, adding the newline trick later would not be backwards compatible, unfortunately. |
If
The rule is: All lambda clauses must start on the same column. Newlines don't matter. That's equivalent to the choice that all statements in a |
Yes -- and I'm keen to get this resolved. Here is the choice we are discussing:
From what I see above I see
Any other opinions, from anyone? Preferably with brief reasoning. Please say within the next week, then I'll get the committee to decide. It's not a big deal, happily. |
Better to keep extensions immutable. Introduce |
I think the GHC committee should write down a clear guideline document on the "immutability of extensions".
-- This works with GHC-8.4 but not with GHC-8.2 or GHC-8.0
-- is accepting this a new feature or a bug fix?
{-# LANGUAGE ApplicativeDo #-}
bar :: Applicative f => f Bool
bar = do
pure True
pure False
Others and I have brought up the problem of extension versioning previously. It would be very nice to have a written document on what to expect. Being consistent (from now on) would be very welcome. (My current expectation is that anything can change at any point, and I use my experience to judge what is likely to change and what isn't. And I have to admit I didn't think the current In the light of previous, and IMHO, simply extending |
-XOverloadedRecordUpdate was added recently and is experimental, and the User guide states that it is intended to be changed in the future:
(though I suppose it's possible that adding |
Fair enough -- why not write to ghc-steering-committee@haskell.org? To me it seems that the tension is:
I incline to the latter, hence suggesting extending Returning to the (1) vs (2) choice above, does anyone else want to express a view? It sounds as if @phadej leans towareds (2) |
If we versioned language extensions --- not such that code could do |
People from outside the community have the (incorrect) impression that language extensions mean that Haskell is in fact several incompatible languages. I don't believe that the incorrect impressions of outsiders should drive our decision making but in this case I really dread the consequences of giving the impression that Haskell is several numbered incompatible languages. |
That is fair. One can similarly argue that listing extensions wastes effort on the fiction that there are non-GHC (-based) compilers for Haskell people actually use :/. |
When the truth of the matter is that it's one, increasingly complicated language. |
A language that maximizes back compat will grow indefinitely. Haskell already grows very fast (too fast for my taste). That just results in a language that is too big to be understandable, filled with too many language constructs or extensions whose combined effect is too hard to predict. A language that maximizes coherence reduces entropy, at the expense of breaking language compatibility. That's why it is important that such changes are rare, coordinated, and deployed in a way that gives people a lot of time and help to adapt. The good consequence is that, when people feel they are being given importance just by being told how the plan affects them and by receiving help to adapt their code, they will feel more included, resulting in an overall more cohesive community to which they will feel compelled to give back. In economic terms, (1) has lower short-term cost but a higher long-term cost, (2) has a higher short term cost but a lower long term cost. However you look at it, if we can afford it, I believe (2) is a better path. |
May I suggest that the general discussion, that is no longer tied to |
@simonpj Since it's been a few weeks, is this ready to go to the committee again to decide on whether to use -XLambdaCase or a new extension? |
It's a difficult call and I have sympathy with all the points raised so far on both sides. But let me argue in favour of (1):
|
@JakobBruenker I am sorry this has taken so long. I have been distracted. I will get it decided asap. |
I took a vote of the steering committee of (1) vs (2) and ended up with (2). So @JakobBruenker could you modify the proposal to say that we'll simply extend the existing Then we can merge. Thanks! |
OK @nomeata could you pull the trigger please? |
Trigger pulled. The conversion from |
@nomeata Looks like the tables have a few instances of the string |
Implemented in https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7873! |
Thanks so much @JakobBruenker for pushing this through! |
Can someone with admin powers edit the initial post to link to the implementation: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7873 |
@tomjaguarpaw done |
The proposal has been accepted; the following discussion is mostly of historic interest.
implemented in https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7873
This proposal introduces a new extension
-XMultiWayLambda
that allows a new expression introduced by\ of
, similar to lambdas but with guards and multiple clauses, enabled by implicit layout.Rendered