Nate Glenn Nate Glenn - 3 months ago 14
Perl Question

Why does the goatse operator work?

The difference between arrays and lists and between list and scalar context have been discussed in the Perl community quite a bit this last year (and every year, really). I have read over articles from chromatic and friedo, as well as this recommended monks node. I'm trying now to understand the goatse operator, documented in perlsecret.

Here is some code I used to study it:

# right side gets scalar context, so commas return rightmost item
$string = qw(stuff junk things);
say $string; # things

# right side gets list context, so middle is list assigned in scalar context
$string = () = qw(stuff junk things);
say $string; # 3

# right side gets list context, so creates a list, assigns an item to $string2, and
# evaluates list in scalar context to assign to $string
$string = ($string2) = qw(stuff junk things);
say $string; # 3
say $string2; # stuff


I think I have come far enough to understand all of the list and scalar context workings. The comma operator in scalar context returns its right side, so the first example simply assigns the last item in the comma expression (without any commas) to
$string
. In the other examples, the assignment of the comma expression to a list puts it in list context, so a list is created, and lists evaluated in scalar context return their size.

There are 2 parts that I don't understand.

First, lists are supposed to be immutable. This is stressed repeatedly by friedo. I guess that assignment via
=
from list to list distributes assignments from items in one list to items in the other list, which is why in the second example
$string2
gets
'stuff'
, and why we can unpack
@_
via list assignment. However, I don't understand how assignment to
()
, an empty list, could possibly work. With my current understanding, since lists are immutable, the size of the list would remain 0, and then assigning the size to
$stuff
in examples 2 and 3 would give it the value 0. Are lists not actually immutable?

Second, I've read numerous times that lists don't actually exist in scalar context. But the explanation of the goatse operator is that it is a list assignment in scalar context. Is this not a counter-example to the statement that lists don't exist in scalar context? Or is something else going on here?

Update: After understanding the answer, I think an extra pair of parentheses helps to conceptualize how it works:

$string = ( () = qw(stuff junk things) );


Inside the parens, the
=
is an assignment to an 'aggregate', and so is a list assignment operator (which is different from the scalar assignment operator, and which should not be confused with "list context"; list and scalar assignment can happen in either list or scalar context).
()
does not change in any way.
=
has a return value in Perl, and the result of the list assignment is assigned to
$string
via the left
=
. Assignment to
$string
gives scalar context to the RHS (everything in the parens), and in scalar context the returned value of the list assignment operator is the number of items in the RHS.

You can put the RHS list assignment into list context instead:

($string) = ( () = qw(stuff junk things) );


According to perlop list assignment in list context returns the list of assigned lvalues, which here is empty since there is nothing to be assigned to in
()
. So here $string would be
undef
.

Answer

It helps to remember that in Perl, assignment is an expression, and that you should be thinking about the value of the expression (the value of the assignment operator), not "the value of a list".

The value of the expression qw(a b) is ('a', 'b') in list context and 'b' in scalar context, but the value of the expression (() = qw(a b)) is () in list context and 2 in scalar context. The values of (@a = qw(a b)) follow the same pattern. This is because pp_aassign, the list assignment operator, chooses to return a count in scalar context:

else if (gimme == G_SCALAR) {
    dTARGET;
    SP = firstrelem;
    SETi(lastrelem - firstrelem + 1);
}

(pp_hot.c line 1257; line numbers are subject to change, but it's near the end of PP(pp_aassign).)

Then, apart from the value of the assignment operator is the side-effect of the assignment operator. The side-effect of list assignment is to copy values from its right side to its left side. If the right side runs out of values first, the remaining elements of the left side get undef; if the left side runs out of values first, the remaining elements of the right side aren't copied. When given a LHS of (), the list assignment doesn't copy anything anywhere at all. But the value of the assignment itself is still the number of elements in the RHS, as shown by the code snippet.