List context and the comma operator in Perl

148 Views Asked by At

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...

4

There are 4 best solutions below

8
Shawn On BEST ANSWER

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.
0
ikegami On

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.
0
zdim On

...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).

0
LanX On

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