10

The documentation says that the comma operator has higher precedence than the assignment = operator, and this is specifically different than in Perl, so that we are allowed to remove parentheses in some contexts.

This allows us to do things like this:

my @array = 1, 2, 3;

What I don't understand is why when do something like this:

sub test() { return 1, 2 }
my ($a, $b);
$a, $b = test();

$b get assigned [1 2] while $a gets no value.

While I would assume that the following would be equivalent, because the comma operator is tighter than the assignment.

($a, $b) = test();

The semantics of Raku have a lot of subtlety and I guess I am thinking too much in terms of Perl.

Like raiph said in the comments, my original assumption that the comma operator has higher precedence than the assignment operator was false. And it was due to a problem in the rendering of the operator precedence table, which didn't presented operators in their precedence order. This explains the actual behavior of Raku for my examples.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
WhiteMist
  • 885
  • 4
  • 13
  • 2
    1 of 2. This is hilarious at least twice over so far. First, me being dumb enough not to guess there was more to this than I was thinking (I'm not yet used to your nick but now will be!) Second, someone has *alphabetically sorted* the table in the doc's Operators page's *main column* (as against the *list on the left side of the page*) but left the verbiage claiming that "The following table summarizes the precedence levels offered by Raku, listing them in order from high to low precedence." If you look down the left hand side you'll see comma precedence is *between* item and list assignment. – raiph May 02 '22 at 20:27
  • 2
    2 of 2. Having just read it, I'd say the situation is explained surprisingly well (imo, though perhaps that's because I know Raku well) in [Item and list assignment](https://docs.raku.org/language/variables#Item_and_list_assignment). – raiph May 02 '22 at 20:31
  • 2
    I'm currently confused/investigating. I went to do a PR to either fix the verbiage claiming they were listed in precedence order or reorder the table. But the current github version of that page has the operator table in the main part of the page listed as the verbiage says, in precedence order! So it seems to be just the *rendered* page with them listed by alphabetical ordering of the precedence level names. Maybe it's just out of date, but I've yet to find *any* version in github with them listed alphabetically. Like I said, I'm currently confused/investigating... – raiph May 02 '22 at 20:53
  • 2
    Doc issue filed: https://github.com/Raku/doc/issues/4071 – raiph May 02 '22 at 21:22
  • @raiph Thanks for "debugging" this issue. It makes perfect sense now. But there are still an issue regarding the operator precedence table IMO. `=` should also be on `List prefix` as well as on `Item assignment`. Because people would not understand the behavior of `my @arrray := 1, 2, 3; ` from the current good version of this table (the pod version). – WhiteMist May 04 '22 at 18:01
  • I just added an issue: https://github.com/Raku/doc/issues/4073 – WhiteMist May 04 '22 at 18:16

1 Answers1

6

The = operator itself is always item assignment level, which is tighter than comma. However, it may apply a "sub-precedence", which is how it is compared to any further infixes in the expression that follow it. The term that was parsed prior to the = is considered, and:

  • In the case that the assignment is to a Scalar variable, then the = operator works just like any other item assignment precedence operator, which is tighter than the precedence of ,
  • In any other case, its precedence relative to following infixes is list prefix, which is looser than the precedence of ,

To consider some cases (first, where it doesn't impact anything):

$foo = 42;     # $ sigil, so item assignment precedence after
@foo = 1;      # @ sigil, so list assignment precedence after
@foo[0] = 1;   # postcircumfix (indexing operator) means list assignment after...
$foo[0] = 1;   # ...always, even in this case

If we have a single variable on the left and a list on the right, then:

@foo = 1, 2;      # List assignment into @foo two values
$foo = 1, 2;      # Assignment of 1 into $foo, 2 is dead code

These apply with the = initializer (following a my $var declaration). This means that:

loop (my $i = 0, my $j = $end; $i < $end; $i++, $j--) {
    ...
}

Will result in $i being assigned 0 and $j being assigned $end.

Effectively, the rule means we get to have parentheses-free initialization of array and hash variables, while still having lists of scalar initializations work out as in the loop case.

Turning to the examples in the question. First, this:

($a, $b) = test();

Parses a single term, then encounters the =. The precedence when comparing any following infixes would be list prefix (looser than ,). However, there are no more infixes here, so it doesn't really matter.

In this case:

sub test() { return 1, 2 }
my ($a, $b);
$a, $b = test();

The precedence parser sees the , infix, followed by the = infix. The = infix in itself is tighter than comma (item assignment precedence); the sub-precedence is only visible to infixes parsed after the = (and there are none here).

Note that were it not this way, and the precedence shift applied to the expression as a whole, then:

loop (my $i = 0, my @lagged = Nil, |@values; $i < @values; $i++) {
    ...
}

Would end up grouped not as (my $i = 0), (my @lagged = Nil, |@values), but rather (my $i = 0, my @lagged) = Nil, |@values, which is rather less useful.

Jonathan Worthington
  • 29,104
  • 2
  • 97
  • 136
  • Perfect. raiph had already corrected me by saying that in fact, the comma operator precedence is in between item and list assignment, but I had still some questions on the rationale. Now it is more clear. – WhiteMist May 06 '22 at 08:55