12

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.

dawg
  • 98,345
  • 23
  • 131
  • 206
Nate Glenn
  • 6,455
  • 8
  • 52
  • 95
  • `($x, $y) = (10, 20)` assigns 10 to `$x` and 20 to `$y`. `($x, $y) = (10, 20, 30)` does the same, but the extraneous 30 is discarded. `() = (10, 20)` is just a degenerate case; *all* the values in the RHS list are discarded. – Keith Thompson Jan 10 '14 at 07:33
  • 1
    See [Mini-Tutorial: Scalar vs List Assignment Operator](http://www.perlmonks.org/?node_id=790129) – ikegami Jan 10 '14 at 13:39

3 Answers3

11

You misunderstand. Lists evaluated in scalar context do not get their size. In fact, it is all but impossible to have a list in scalar context. Here, you have a scalar assignment with two operands, a scalar variable on the left, and a list assignment on the right (given scalar context by the scalar assignment). List assignments in scalar context evaluate to the number of items on the right of the assignment.

So, in:

1 $foo
2 =
3 ()
4 =
5 ('bar')

2, a scalar assignment, gives 1 and 4 scalar context. 4, a list assignment, gives 3 and 5 list context, but nevertheless is itself in scalar context and returns appropriately.

(When = is a list assignment or a scalar assignment is determined purely from the surrounding syntax; if the left operand is a hash, array, hash slice, array slice, or in parentheses, it is a list assignment, otherwise it is a scalar assignment.)

This treatment of list assignments in scalar context makes possible code like:

while ( my ($key, $value) = each %hash ) {

where list-context each is an iterator that returns (in list context) one key and value for each call, and an empty list when done, giving the while a 0 and terminating the loop.

ysth
  • 96,171
  • 6
  • 121
  • 214
  • So assignment to `()` is used to make the right side return a list value first. Then the list value is assigned via scalar `=`, returning the number of items. So the "scalar" part of this is the assignment operator, not the list itself. Also `while` gives scalar context. I see that `($foo, $bar) = qw(foo bar); print scalar(($foo, $bar) = ())` prints `0`. So we can have list assignment in scalar context and scalar assignment in list context. Eek. – Nate Glenn Jan 10 '14 at 21:53
  • I'm having trouble wrapping my head around how 2 affects 1 and 4. Shouldn't 1 be giving scalar context to 2, and 3 be giving list context to 4? And how does it work that 4 assigns a list to a list, but in scalar context? Wouldn't that make the value of 5 be simply `1` (the number of elements), resulting in a `$foo` value of `1`? – Nate Glenn Jan 10 '14 at 21:57
  • no. operators are the source of context, always. not operands. though syntactically, 1 makes 2 a scalar assignment and 3 makes 4 a list assignment4, that isn't context, it's a pre-context stage of compilation. and context determines what the list assignment *returns*, not what it *does*. does that help? – ysth Jan 12 '14 at 03:09
  • Yep! Thanks. Can you think of any good document that discusses all of that in detail? Operand, operator, context, compilation order, etc. – Nate Glenn Jan 12 '14 at 04:22
10

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.

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • Thanks! This is good info. Can you add file and line number for that reference? – Nate Glenn Jan 12 '14 at 01:09
  • btw, I see that you gave me the checkmark. I think that ikegami and ysth deserve tons of credit for their answers as well; I just did my best to boil it down to the essentials. – hobbs Jan 12 '14 at 01:40
  • Yeah I hope they aren't offended. They are great answers and I hope they are upvoted lots. I thought that yours really brought it all together and would be best to read first for someone just finding this question. – Nate Glenn Jan 12 '14 at 04:18
6

First, "list" is an ambiguous term. Even the question uses it to refer to two different things. I suspect you might be doing this without realizing it, and that this is a significant part of the cause of your confusion.

I shall use "a list value" to denote what an operator returns in list context. In contrast, "the list operator" refers the operator EXPR,EXPR,EXPR,... also known as "the comma operator"[1].

Second, you should read Scalar vs List Assignment Operator.


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.

Correct.

I've read numerous times that lists don't actually exist in scalar context.

That wording is very ambiguous. You seem to be talking about list values (which are found in memory), but scalar context only exist in the code (where operators are found).

  • A list/comma operator can be evaluated in scalar context.
  • A list value can't be returned in scalar context.

Scalar context is a context in which an operator can be evaluated.

A operator evaluated in scalar context cannot return a list. It must return a scalar. Loosely speaking, you could say a list can't be returned in scalar context.

On the other hand, a list/comma operator can be evaluated in scalar context. e.g. scalar(4,5,6). Every operator can be evaluated in any context (though it's not necessarily useful to do so).

But the explanation of the goatse operator is that it is a list assignment in scalar context.

It includes one, yes.

List values and list assignment operators are two different things. One's a value. The other is a piece of of code.

A list assignment operator, like every other operator, can be evaluated in scalar context. A list assignment operator in scalar context returns the number of scalars returned by its RHS.

So if you evaluate () = qw(a b c) in scalar context, it will return three, since qw() placed three scalars on the stack.

However, I don't understand how assignment to (), an empty list, could possibly work.

Just like the assignment ($x,$y) = qw(stuff junk things) ignores the third element returned by the RHS, () = qw(stuff junk things) ignores all elements returned by the RHS.

With my current understanding, since lists are immutable, the size of the list would remain 0

Saying "the size of the list would remain zero" for ()=qw(a b c) is like saying "the value of the scalar will remain 4" for 4+5.

For starters, there's question of which list you're talking about. The LHS returned one, the RHS returned one, and the assignment operator might return one.

The list value returned by the LHS will be 0 in length.

The list value returned by the RHS will be 3 in length.

In scalar context, the list assignment operator returns the number of scalars returned by RHS (3).

In list context, the list assignment operator returns the scalars returned by LHS as lvalues (empty list).

lists are supposed to be immutable.

If you're thinking in terms of list mutability, you took a wrong turn somewhere.[2]


Notes:

  1. The docs call EXPR,EXPR,EXPR,... two instances of a binary operator, but it's easier to understand as a single N-ary operator, and it's actually implemented as a single N-ary operator. Even in scalar context.

  2. It's not actually true, but let's not go any further down this wrong turn.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • nice answer, though I was confused by the introduction of the term "list operator"; I don't quite know what that means. – ysth Jan 10 '14 at 17:21
  • @ysth, That's what Perl calls it. That's what people call it. That's what the OP called it. Do you call the code `a,b,c` something other than a list? perlop calls is a comma operator, but it was key to the answer to continue calling it the list operator. (perlop also says it's a binary operator. It also says it's not an operator at all but a separator in list context, which is impossible since only operators and subs can be evaluated in list contexts.) – ikegami Jan 10 '14 at 17:30
  • @ysth, Switched to using "list/comma operator". I normally do, but forgot here. – ikegami Jan 10 '14 at 17:43
  • Thank you! Your link and explanation were extremely helpful. [This](http://www.perlmonks.org/?node_id=527973) node was linked with the other one and really solidified everything for me before coming back here to read your explanation. Perhaps you could elaborate on list mutability a bit? friedo says they are immutable in that article. – Nate Glenn Jan 11 '14 at 01:12