Skip to content
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

RakuAST: implement for { } otherwise { } #5390

Closed
wants to merge 1 commit into from
Closed

Conversation

lizmat
Copy link
Contributor

@lizmat lizmat commented Sep 22, 2023

I was looking at what would need to be done to port the Slang::Otherwise module to work with RakuAST, and realized it was probably less work to just implement the functionality in the Raku grammar.

This does:

  • Implement a :otherwise argument to RakuAST::Statement::For
  • Adapts the QAST generation accordingly if an otherwise was found
  • Adds preliminary grammar support for "otherwise"

This does NOT adapt .raku or .DEPARSE yet.

It also doesn't answer the question on whether "otherwise" should be a 6.e feature, or should also be available in 6.c and 6.d. It feels that it should also be available in 6.c and 6.d so that users of the Slang::Otherwise module can just remove it, and/or have the Slang::Otherwise module not do anything if it spots that it is being called with the Raku grammar.

I was looking at what would need to be done to port the Slang::Otherwise
module to work with RakuAST, and realized it was probably less work to
just implement the functionality in the Raku grammar.

This does:
- Implement a :otherwise argument to RakuAST::Statement::For
- Adapts the QAST generation accordingly if an otherwise was found
- Adds preliminary grammar support for "otherwise"

This does NOT adapt .raku or .DEPARSE yet.

It also doesn't answer the question on whether "otherwise" should be
a 6.e feature, or should also be available in 6.c and 6.d.  It feels
that it should also be available in 6.c and 6.d so that users of the
Slang::Otherwise module can just remove it, and/or have the
Slang::Otherwise module not do anything if it spots that it is being
called with the Raku grammar.
@lizmat lizmat marked this pull request as draft September 22, 2023 21:56
@2colours
Copy link
Contributor

2colours commented Sep 22, 2023

It's probably always "less work" to implement a slang in the main language, or at least not more work. That isn't a sufficient reason to add syntax to the whole language, possibly even retrospectively.

What was the process for introducing this language change? What is the rationale? Who wants to use it? Out of the people who may use it or are willing to use it, who thinks that it should be in the main grammar?

Hoping that there will be some sufficient decision process, rather than a PR silently getting merged, I would argue against this change.

  1. The obvious one: the language is already huge and there is no free dinner. This will become another thing to maintain (and keep stable), another thing to parse, another thing to generate code for, optimize, etc.
  2. Same topic, from the user point of view. I think it's common human mindset that you try to use the language to the fullest extent, especially with a language that attracts people who take pride in clever code, and condition newcomers to the same thing. So it would actually make a difference for everyone who wants to read code; it would further increase the mental load of the language for everyone. There should be some massive gain to counter these points that naturally stand against change, and especially extension. I can't see any such gain, only a kind of clever shortcut.
  3. The semantics is not intuitive, or at least very much "culture dependent". I think (I hope?) we can agree that "otherwise" doesn't have a natural meaning for iteration/loops; in general, this logical dichotomy doesn't, regardless whether we call it "otherwise" or "else".
    Before approaching Raku, I have never seen anything like this, and more importantly, I have never felt that this would be practical in the first place. (Compare that with next and Slips in map, the concept of Block as callable code or blocks that can return from the enclosing scope.) Seemingly, people with Perl background are more likely to empathize with this one. However, as somebody who used Python before, I empathize much more with Python's completely different else clause for loops which executes when the condition of the loop makes the loop stop. The point is, different people can expect different behavior from a construct like this, and furthermore it could lead to debates what behavior is "the right one".
  4. About the behavior: really, why is it a game changer? In Raku, there are loads of generic and widely applicable tools to bind an expression to a symbol, including given, andthen and .&{...}. One can really just basically write an if for the empty check, and a loop. No nesting is needed, no else is needed. If somebody comes across this pattern in the code flow often (which, again, I absolutely don't), and they even feel that this pattern doesn't satisfy them, I think the perfectly appropriate solution is really a slang.
    It's easy to construct similar minor inconveniences that could have a custom solution: for example, one thing I semi-regularly come across is that if a certain expression satisfies a certain predicate, I want to run code where the value of the expression is available. Because of the predicate, I can't use the pointy parameter of the if, so I typically end up setting up a symbol separately, e.g with a given.
  5. It seems surprisingly-not-so-consistent to add an otherwise clause only for one type of loops, without even providing an equivalent for map. To me, this signals that it's not such a useful abstraction. For map, maybe it is even "too easy" to do without? Anyway, I think there are well-understood valid reasons to not add otherwise for loop and let alone until, and it doesn't get proposed for while either. If these valid reasons outweigh one's natural intuition for code that works alike and reads alike - actually, I'm going to quote a couple of related catchphrases from Larry Wall - then maybe the simple YAGNI reasons can also outweigh adding this syntax to the main grammar in the first place.
    • ☞ If you’re gonna generalize, do it harder, but not too hard.
    • ☞ Replace weak features with stong ones that degenerate to the weak case.
    • ☞ If you’re going to reduce the power of a construct, maybe remove it instead.

@0racle
Copy link
Contributor

0racle commented Sep 23, 2023

As author of the module, I thought I should comment.

Regarding "culture"... Part of the culture of Raku is way it names thing; we like to have a little fun with names. If you are too serious, there are plenty of languages to suit your level of seriousness. Trying to make Raku like those languages erodes what makes us unique. A lot of people coming to the language don't know what comb, toggle, grep, rotor, squish, etc. mean in context either, but you learn the language.

I also don't think map needs an otherwise block just because for loop does. I'm on the fence about whether while loops would benefit from an otherwise block. Regarding writing an if for the empty check, Damian makes the case in his original post that it ends up adding a lot of cruft. It's also why my README uses the example of dir. Without the otherwise construct, one would need to assign to a list and then check for empty.

The part where I partly agree is... even as the author of that module, I don't use it much. I published the module primarily because Damian didn't. I'm not sure it enjoys the broad usefulness of other constructs, but then, Raku has a lot of constructs which I don't use, so maybe I'm a bad sample. This doesn't mean those constructs are not useful.

So where do I stand? Unfortunately, I don't have strong feelings either way. I guess it depends on a few factors: -

  • Do enough people desire this behavior?
  • Would code in the core also benefit from this construct?
  • Would this add much maintenance burden?

As for a language additions in general... Maybe there should be a formal language enhancement process for users, but in almost any language, the designers and maintainers ultimately decide what gets implemented, so I guess I lean in the direction of being fine with it being added.

@2colours
Copy link
Contributor

I think it could just derail the discussion if we got too much into the "fun with the names" part; iffy-diffy-fiddly-gobbling-etc. come to mind. I do think they backfire a lot when you are a newcomer or not an English native, or worse yet, a combination of both. I also think they contribute to a club mentality that doesn't go very well with the inclusivity... but I told myself not to digress too much.

The problem with this name isn't that it's "funny". It's that it tries to give meaning to a "colorless green ideas sleep furiously" type nonsense. This could only work reasonably well if everybody agreed on the same meaning, in which case the nonsense would suddenly turn into a clear thing, even if unusual. However, Python already tried to assign a meaning to this nonsense, and it is a source of confusion and criticism for Python as well. I don't think it requires wild imagination to foresee people trying to use it the way it works in Python.

You may say that the points I raised are not very big problems. Sure, it's not like the world is gonna end. However, they seem good enough points for me to think it comes out as a net negative. An unintuitive structure that saves a couple of keystrokes in highly specific scenarios at the expense of more things to maintain and learn to read.

Without the otherwise construct, one would need to assign to a list and then check for empty.

I covered this in the fourth point. There are loads of widely available generic structures to mitigate a situation like this, and it's very easy to imagine semi-generic-semi-specific situations where you want to avoid an extra assignment but the language provides no syntax for that case.

I would rather not see a precedent for adding stuff like this without a solid reasoning and support from the community (preferably both, to be honest, because we all come with our biases). And I would rather not see precedents of syntax added in pull requests without even some sort of announcement or Github issue.

but in almost any language, the designers and maintainers ultimately decide what gets implemented

It would be okay if the Raku community had a culture of "designers and maintainers" as a definite group who have the authority and willingness for the responsibility that it implies. Since there is basically nobody taking up on Problem solving issues, nor is there a workflow for things like this, I don't think there is any visible intention to govern the language. It's not a figure of speech when I ask about the process, or how this design decision comes to be; I really don't know which "hat lizmat is wearing" when opening a PR like this. A mere contributor? A core developer? A RakuAST designer? Somebody who has a commit bit in the rakudo repository? A member of the Steering Council? The person who probably has the most access rights and connections around what one may call "official Raku platforms"? The answer to this question has implications about how this PR should be taken, and how it relates to other common-interest matters that seemingly nobody even feels responsible for.

@niner
Copy link
Collaborator

niner commented Sep 23, 2023

Please let's stay with implementing the already large enough Raku grammar before adding new features.

@lizmat
Copy link
Contributor Author

lizmat commented Sep 23, 2023

Jeez. This is I guess one of those cases where the discussion is going to be mindblowingly protruded. Almost enough for me to just close this PR and go on indefinite holiday.

Anyways. There's the blog post and the discussion. The fact that Slang::Otherwise has 5 stars on Github is some indication that the repo is appreciated by some people at least.

Some comments:

I can't see any such gain, only a kind of clever shortcut.

I think the blog post shows quite a few use cases, and the reasoning why it would be a good feature to have.

However, as somebody who used Python before, I empathize much more with Python's completely different else clause for loops which executes when the condition of the loop makes the loop stop. The point is, different people can expect different behavior from a construct like this, and furthermore it could lead to debates what behavior is "the right one".

That's why it's called otherwise and not else.

why is it a game changer?

Who said anywhere that this would be a game changer? Not in the blog post, nor in the comments on Reddit, nor in the PR?

One can really just basically write an if for the empty check, and a loop.

Could you give an example of that, that wouldn't affect the laziness of the for loop?

I think there are well-understood valid reasons to not add otherwise for loop and let alone until, and it doesn't get proposed for while either.

I think it could be argued that otherwise be added to loop, until, while, and repeat... as well. I didn't want to spend time on that just yet, but will gladly do so if there's a decision to do this.

I think it could just derail the discussion if we got too much into the "fun with the names" part; iffy-diffy-fiddly-gobbling-etc. come to mind.

Then please don't. Most users will never have to deal with "iffy-diffy-fiddly-gobbling", as these are mostly internal grammar concepts. And maybe some users may see references to them in error messages. And maybe the documentation in the glossary could use some improvements. Please make a doc issue / PR for them if you think the documentation is insufficient.

a "colorless green ideas sleep furiously" type nonsense

Please refrain from using words such as "nonsense" in these discussions. I've taken time to look into adapting the slang, writing the support for it in core, make a PR. The PR may not be accepted, but that is life. But it is NOT nonsense. So please refrain from calling it nonsense.

Without the otherwise construct, one would need to assign to a list and then check for empty.

Indeed, and thus lose the laziness of the iteration. Please provide an example that would maintain the laziness that would NOT involve setting a flag in each iteration or a FIRST phaser.

And I would rather not see precedents of syntax added in pull requests without even some sort of announcement or Github issue.

Why? We have a discussion here, don't we? People can look at the code and its features. That's a lot less vague than making an issue.

I really don't know which "hat lizmat is wearing" when opening a PR like this

The fact that I opened this as a PR, just like anybody can do with a Github account, means I did this as a contributor. Please treat it as such.

Please let's stay with implementing the already large enough Raku grammar before adding new features.

The reason for me to make this PR, was that I realized that to be able to make the Slang::Otherwise slang work with RakuAST, I needed to make changes to the RakuAST::Statement::For class anyway.

@lizmat
Copy link
Contributor Author

lizmat commented Sep 24, 2023

FWIW, 9b29fdcab6 implements the hooks in the RakuAST::Statement::For class to allow the Slang::Otherwise module to work correctly under RakuAST.

@gfldex
Copy link
Contributor

gfldex commented Sep 25, 2023

If I wanted to do stuff when a loop wasn't run, I would do the following:

    my Bool $flag;
    for () {
        FIRST $flag = True;
    }
    do { say 'stuff' } unless $flag;

My Raku-instinct is asking me to use a phaser when doing special stuff with a loop. Maybe that is the right thing to do here. Have a phaser that is run when the loop body isn't.

    for () {
        EMTPY { say 'doing stuff on empty collections'; }
    }

Very well possible that an EMPTY-phaser could be used elsewhere in the language. Also, phasers are copy-pasta-friendly. And I do like myself a good copy-pasta.

@vendethiel
Copy link
Contributor

My Raku-instinct is asking me to use a phaser when doing special stuff with a loop. Maybe that is the right thing to do here. Have a phaser that is run when the loop body isn't.

That looks utterly confusing: you're in a loop body when the loop doesn't run. That also means binding(s) from -> won't exist.

This PR is a good solution to a very common code pattern that simplifies writing it, makes sure side effects aren't evaluated twice, it doesn't have python's weird else behavior (and it doesn't reuse its keyword to make it think it does).

@FCO
Copy link
Member

FCO commented Sep 26, 2023

I like the phaser suggestion... maybe I just don't like the suggested name of that.

@FCO
Copy link
Member

FCO commented Sep 26, 2023

But maybe that would make someone expects to:

if ... {
    EMPTY {
        say "just like an else"
    }
}

To behave like an else... now I'm confused...

@2colours
Copy link
Contributor

This PR is a good solution to a very common code pattern that simplifies writing it, makes sure side effects aren't evaluated twice, it doesn't have python's weird else behavior (and it doesn't reuse its keyword to make it think it does).

Well, what can I say, I agree with none of it basically:

  • it's not even a common code pattern, let alone "very common"
  • Python's behavior is not "weird", in fact, I think it makes considerably more sense and it is surely a more thought-out abstraction as it works with all the loop structures not only in Python (which means 2) but all the non-negated loop structures of Raku as well
  • and while it doesn't reuse the very same word, it literally uses a synonym. I don't get the whole point in a deliberately redundant language; how can you say that you disambiguated something by using a synonym?

@lizmat
Copy link
Contributor Author

lizmat commented Sep 26, 2023

@2colours you already made your opinion clear. Why do you feel the need to re-iterate that if someone has a different opinion?

It does not strengthen your argument.

@2colours
Copy link
Contributor

@lizmat there is a difference between making arguments and stating an opinion as a fact to be taken at face value. I don't want that someone takes the upper hand without arguments simply by asserting confidence. Anyway, I wanted to address "the article" so please wait. There are differences between "opinions".

@lizmat lizmat closed this Sep 26, 2023
@lizmat lizmat deleted the lizmat-otherwise branch September 26, 2023 11:41
@rakudo rakudo locked and limited conversation to collaborators Sep 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants