1

Having some significant confusion about what it means to "evaluate in list context", specifically related to the comma operator. In the linked perlop doc, it says: In list context, it's just the list argument separator, and inserts both its arguments into the list. However, the code

@y = 9, 8, 7, 6;
say @y, ', ', scalar @y;

gives output 9, 1. This respects the fact that, when used as a binary operator on scalar values (?), the comma operator , has lower precedence that the assignment = operator. But since I'm assigning to the list @y, shouldn't the right hand side of the assignment be evaluated in list context and, by the above quote, treat the comma simply as a separator?

I suspect that I just don't truly understand what it "evaluate in list context" means, precisely...

zdim
  • 64,580
  • 5
  • 52
  • 81
fmg
  • 813
  • 8
  • 18

4 Answers4

6

The problem has nothing to do with context.[1] It's a precedence issue.

Assignment has higher precedence than comma, so

my @y = 9, 8, 7, 6;

means

( my @y = 9 ), 8, 7, 6;

but you want

my @y = ( 9, 8, 7, 6 );

Note that the parens do nothing but override precedence.

Warnings would have caught this. Always use use strict; use warnings; or equivalent!


I suspect that I just don't truly understand what it "evaluate in list context" means, precisely...

Each operator decides what to do and what to return based on the context in which they are evaluated.

It does not, however, change how code is parsed.


  1. my @y on the left is enough to cause the list assignment to be used, and is thus enough to cause the RHS to be evaluated in list context. See Scalar vs List Assignment Operator.
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

If you turn on warnings like you should, perl will give you some hints as to what's going on:

#!/usr/bin/env perl
use warnings;
use strict;
use feature qw/say/;

my @y = 9, 8, 7, 6;
say @y, ', ', scalar @y;

running it shows

Useless use of a constant (8) in void context at foo.pl line 6.
Useless use of a constant (7) in void context at foo.pl line 6.
Useless use of a constant (6) in void context at foo.pl line 6.
9, 1

@y = 9, 8, 7, 6 is an example of the comma operator in scalar context, and, as you noted, precedence rules. It's parsed as four different sub-expressions separated by commas: first @y = 9 assigns a single-element list to @y, and then the expressions 8, 7 and 6, all of which do nothing and generate warnings, and the whole thing would return 6 if there was anything to return to. If you want to assign a list to the variable, that list needs to be in parenthesis:

my @y = (9, 8, 7, 6); # Or qw/9 8 7 6/ etc.
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • So the left hand side of the assignment being @y isn't enough to convince the interpreter to evaluate the right hand side in list context? The parentheses are the trigger that causes the switch to list context? – fmg Apr 01 '23 at 22:26
  • 1
    @fmg Basically, yes. The LHS of the assignment operation doesn't determine context. – Shawn Apr 01 '23 at 22:33
  • It can't just be the parentheses, though, because e.g. `sub g {say @_;} g 1, 2, 3, 4;` outputs `1234`, so the function is picking up the list 1, 2, 3, 4 even though it's not parenthesized. Could you point me to a document that explains in some technical detail what determines context? – fmg Apr 01 '23 at 22:44
  • @fmg Function arguments are list context by default (This can be changed with a [prototype](https://perldoc.perl.org/perlsub#Prototypes)). – Shawn Apr 01 '23 at 23:05
  • I realize there's a precedence point here too (?) -- list operators (rightward) have lower precedence than commas, the the function invocation picks up the whole list, right? – fmg Apr 01 '23 at 23:20
  • 1
    @fmg, Re "*So the left hand side of the assignment being @y isn't enough to convince the interpreter to evaluate the right hand side in list context?*" No. /// Re "*The parentheses are the trigger that causes the switch to list context?*" No. /// The parens just change precedence. `my @y = 9, 8, 7, 6;` means `( my @y = 9 ), 8, 7, 6;` but you want `my @y = ( 9 , 8, 7, 6 );` – ikegami Apr 02 '23 at 01:37
  • 2
    @Shawn Re "*Basically, yes. The LHS of the assignment operation doesn't determine context.*", That's completely wrong. The LHS of the assignment 100% determines the operator used (list assignment or scalar assignment) and thus the context of the RHS. See [Scalar vs List Assignment Operator](https://stackoverflow.com/a/54564429/589924) – ikegami Apr 02 '23 at 01:50
  • @Shawn: the LHS determines context, the 9 - or anything else - is evaluated in list context. The problem here is - like Ikegami already explained - precedence. – LanX Apr 02 '23 at 18:45
2

...shouldn't the right hand side of the assignment be evaluated in list context...

Just to emphasize this: yes, it should and it is -- after precedence rules are applied first.

See that with

perl -wE'my @ary = localtime, qw(a b); say for @ary'

It prints two warnings for Useless use of a constant... (for a and b), and then the values returned when localtime runs in a list context (and not the timestamp string, what it does in a scalar context).

zdim
  • 64,580
  • 5
  • 52
  • 81
0

The array at the LHS of an assignment is actually enforcing list context [1], but the comma is not part of the RHS because of precedence. That's why in @y = 9, 8, 7, 6; the commas are actually in void context. Ikegami and others already explained this.

But to answer your main question:

I suspect that I just don't truly understand what it "evaluate in list context" means, precisely...

Yes I have to admit that's a bit confusing here.

For better understanding, here an example where you can see the effect of the three main contexts in Perl on a literal comma separated list.

use strict;
use warnings;
use Carp;


sub ctxt {
    my $wa = wantarray // 2;
    carp (("SCALAR","LIST","VOID")[$wa]);
}


sub commas {
    return "A","B","C","D"
}

my @arr;

# --- List context
@arr = ctxt();
@arr = commas();
warn "<@arr>";

# --- Scalar context
@arr = scalar ctxt();
@arr = scalar commas();
warn "<@arr>";

# --- Void context
ctxt();
commas();
LIST at d:/Perl/pm/context.pl line 8.
    main::ctxt() called at d:/Perl/pm/context.pl line 19
<A B C D> at d:/Perl/pm/context.pl line 21.
SCALAR at d:/Perl/pm/context.pl line 8.
    main::ctxt() called at d:/Perl/pm/context.pl line 24
<D> at d:/Perl/pm/context.pl line 26.
VOID at d:/Perl/pm/context.pl line 8.
    main::ctxt() called at d:/Perl/pm/context.pl line 29

The callers context propagates to the return of a sub-routine, that's why you see the complete LIST in list-context and only the last element when the scalar comma operator is used.

ctxt() is just a helper function I hacked, which you can use to find out which context you are dealing with in the future.

HTH! :)


  1. it literally compiles to a list-assigment operator
LanX
  • 478
  • 3
  • 10