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

Implement +=, *= and all the other assignment operators #152

Open
masak opened this issue Jun 17, 2016 · 11 comments
Open

Implement +=, *= and all the other assignment operators #152

masak opened this issue Jun 17, 2016 · 11 comments

Comments

@masak
Copy link
Owner

masak commented Jun 17, 2016

I'm thinking we first do this one behind a feature flag, then (when we have modules) as a pragma that flips on the feature flag, and finally as a pure-007 parser-changing module.

It'll need to be implemented using something like a "what are all the allowable infixes" hook, which adds all the assignment infixes in a dynamic-enough way that (say) defining a new ordinary infix op immediately makes the corresponding op= form available.

Also, remember that the parsing of op= is such that the entire rest of the expression has invisible parentheses. So, for example

my four = 4;
say(four * 10 + 3);    # 43
say(four *= 10 + 3);   # 52
@masak
Copy link
Owner Author

masak commented Sep 8, 2016

It'll need to be implemented using something like a "what are all the allowable infixes" hook, which adds all the assignment infixes in a dynamic-enough way that (say) defining a new ordinary infix op immediately makes the corresponding op= form available.

The cool thing is that is parsed gives us this for free. This is where "is parsed regexes are scoped to the (current language's) parser" pays off — no hook needed; we just parse and the infix is there.

macro postfix:<op=>(term, infixop, expr) is parsed(/ <infix> "=" <EXPR> /) {
    return quasi {
        {{{term}}} = {{{term}}} {{{infixop @ Q::Infix}}} {{{expr}}}
    }
}

Also, remember that the parsing of op= is such that the entire rest of the expression has invisible parentheses.

This also falls out of is parsed; the <EXPR> inside of it gobbles up everything, guaranteeing that there won't be an operator when it's done.

@masak
Copy link
Owner Author

masak commented Jun 22, 2018

If we go ahead and make assignment a statement form, I guess this one becomes a statement form too.

@masak
Copy link
Owner Author

masak commented Jun 22, 2018

Even here, lvalues get involved in a big way. Any side effect happening in the path of the lhs should happen once, which relates both to the Single Evaluation Rule and to the Assignment Protocol.

@masak
Copy link
Owner Author

masak commented Aug 8, 2018

Also, remember that the parsing of op= is such that the entire rest of the expression has invisible parentheses.

I think this is wrong, but without observable consequences, so to speak.

infix:<=> is of loosest precedence. op= would be an infix op of the same precence. So, in effect, the rest of the expression, being tighter or of equal precedence, would parse as if it had invisible parentheses.

New attempt at implementation:

macro infix:<op=>(lhs, rhs, infixop) is equiv(infix:<=>) is parsed(/ <infix> "=" /) {
    quasi {
        my L = lvalue({{{lhs}}});
        L.write(L.read() {{{Q.Infix @ infixop}}} {{{rhs}}});
    };
}

@masak masak changed the title Implement +=, ::= and all the other assignment operators Implement +=, *= and all the other assignment operators Aug 8, 2018
@masak
Copy link
Owner Author

masak commented Aug 20, 2018

Heh. The new implementation is left-recursive. Not a showstopper in itself, of course, but... interesting.

  • Easiest would be if the parser is able to take left-recursive things in stride. I gather a recursive-descent parser finds that bit challenging. Here I was gonna say "A Thompson engine would do fine with this, I think", but these aren't regular expressions we're talking about, since we have <infix> calling <infix>. That's some kind of pushdown thing.

  • There's also the question of how we allow += but disallow +==. There's nothing wrong in principle with metaoperators acting twice; it's just in this case it doesn't make sense. This is where Perl 6 says that it can't make assignment out of += because += is too diffy.

@masak
Copy link
Owner Author

masak commented Aug 27, 2018

  • There's also the question of how we allow += but disallow +==.

It's possible is parsed(/ <infix> <!after "="> "=" /) would be enough here. (Assuming the above-mentioned left recursion is handled.)

@masak
Copy link
Owner Author

masak commented Nov 7, 2018

That brings up another point. The "chaining operators" in Perl 6 (corresponding to all the comparison ops in 007) are "too diffy" to be assignmentopified.

Perl 6 has a precedence level called "structural infix" which 007 doesn't really have. (But might when it gets for example .. for ranges.) That one is diffy too.

Perhaps it would make sense to attach some metadata to precedence levels? That's a very unfinished thought, so I'll just leave that as it is.

@masak
Copy link
Owner Author

masak commented May 11, 2019

The new implementation is left-recursive. Not a showstopper in itself, of course, but... interesting.

Just noting in passing that this could be handled by adopting the infix:postfix:<=> idea from #430.

There's also the question of how we allow += but disallow +==. There's nothing wrong in principle with metaoperators acting twice; it's just in this case it doesn't make sense. This is where Perl 6 says that it can't make assignment out of += because += is too diffy.

I think we should adopt the diffy categorization wholesale. It works. Precedence levels aren't first-class, but we can put a @Diffy annotation on individual operators, as usual. I'm fine with only allowing @Diffy annotations on the first defined operator of a precedence level.

@masak
Copy link
Owner Author

masak commented Aug 2, 2019

The new implementation is left-recursive. Not a showstopper in itself, of course, but... interesting.

Just noting in passing that this could be handled by adopting the infix:postfix:<=> idea from #430.

I came here to make that same observation again, not knowing whether I'd made it before.

It is possible that infixes are "simple" enough (just an array of literal strings, right?) that a parsed infix:<op=> rule might just power through them all an extend the declarative prefix all the way to the =... but it feels like a dangerous game to play, and #430 feels like much better bet.

@masak
Copy link
Owner Author

masak commented Feb 15, 2021

Another thing. This blog post at 2ality explains how the new JavaScript logical-assignment operators (&&=, ||=, and ??=) do not follow the usual pattern of A op= B meaning A = A op B, but instead mean A op (A = B).

The reason for this (as explained in that post) is that the original logical operators are all short-circuiting, and the assignment versions should be too.

Interestingly, Raku does not go this route — a synopsis has a phrasing that amounts to "macroish operators lose their short-circuitingness as they are turned into assignment operators". That may be consistent, but it's not strangely consistent, if you see what I mean. I much prefer JavaScript's strategy here, even though it clearly means having two separate rules.

@masak
Copy link
Owner Author

masak commented Apr 19, 2023

  • There's also the question of how we allow += but disallow +==. There's nothing wrong in principle with metaoperators acting twice; it's just in this case it doesn't make sense. This is where Perl 6 says that it can't make assignment out of += because += is too diffy.

Actually, it's because it's too fiddly. (Duh!)

But this actually does solve the whole thing, like providing a base case against an infinite recursion; each thus created metaoperator only acts on operators which are not fiddly, but the created metaoperator itself is too fiddly and therefore not subject to further meta-ing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant