13

A list assignment in scalar context returns the number of elements on the right hand side:

scalar(my ($hello, $there, $world) = (7,8)); #evaluates to 2

Why does it evaluate the right hand side and produce 2, instead of the newly defined list being evaluated and returning 3?

To me, it seems like $hello gets 7, $there gets 8, and $world gets undef, then that list is evaluated in scalar context, which would result in 3, as that is the number of elements in the list ($hello $there $world). It seems weird to me that context affects which part of the evaluated expression is returned:

my $greeting = (($hello, $there, $world) = (7,8)); #2

my @greeting = (($hello, $there, $world) = (7,8));
my $greeting_length = @greeting; #3
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Brian
  • 1,571
  • 1
  • 15
  • 20
  • 1
    Indeed, no: If the left-hand list was the thing evaluated in scalar context, the value would be `undef`. A list is not an array. – darch Feb 17 '12 at 00:32
  • 1
    @darch, That make no sense. `($hello, $there, $world)` (and `(7,8)`) cannot be executed in scalar context in `($hello, $there, $world) = (7,8)`. The list assignment could return 3 if it wanted to, but there's no reason to. – ikegami Feb 17 '12 at 05:11
  • I was responding precisely to the phrase "then [the list ($hello, $there, $world)] is evaluated in scalar context". Evaluate `my ($h, $t, $w) = (7, 8, undef); scalar ($h, $t, $w)` and see that lists are not arrays. – darch Feb 17 '12 at 20:06
  • @darcy `scalar($h, $t, $w)` is the same as `scalar($w) ` ... that's a sequence operator, not a list. RTFM: http://perldoc.perl.org/functions/scalar.html – Jim Balter Oct 09 '18 at 18:55

3 Answers3

16

It's documented to count the elements on the right in perlop (the last sentence in the Assignment Operators section):

Similarly, a list assignment in list context produces the list of lvalues assigned to, and a list assignment in scalar context returns the number of elements produced by the expression on the right hand side of the assignment.

The reason it works like that is so that you can write things like this:

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

If it counted the number of elements on the left hand side of the assignment, that would be an infinite loop.

If you think about it, the number of elements on the left hand side is either the same as on the right hand side or it's a constant (when you're assigning to a list of scalars). In the first case, it makes no difference which side you count, and in the second case, counting the right hand side is more useful.

On the other hand, in list context the assignment operator returns the left hand list, because that's more useful. If you use it in a context that modifies the list elements, you want to modify the variables that were just assigned to.

Re: your comment In your example, (7,8) is a two-element list, which is why the assignment operator returns 2. When you assign a shorter list to a longer list of scalars, the right hand side is not "padded out" with undef before the assignment happens. Instead, any variables that did not have a value associated with them from the right hand list are reset to their default value. For a scalar variable, that's undef. For arrays, that's an empty array. For hashes, that's an empty hash.

cjm
  • 61,471
  • 9
  • 126
  • 175
  • Your response make sense, I understand why it works this way, but I'm more interested in how it works. See my comment on ikegamis post. – Brian Feb 16 '12 at 20:27
  • @Brian, in list context, you get the left hand list, but in scalar context you get the count of the right hand list. In both cases, it's because that's the most useful thing to return. See my updated answer for details. – cjm Feb 16 '12 at 21:20
  • 1
    @Brian, there are many functions and operators in Perl that in scalar context return something other than the number of elements they would return in list context. You have to read the docs to find out what a function or operator will return. – cjm Feb 16 '12 at 21:23
  • "I understand why it works this way, but I'm more interested in how it works." -- This is a meaningless statement. It works this way because the language specification says it does. How it works is via the perl implementation. – Jim Balter Oct 09 '18 at 19:02
7
It seems weird to me that context effects which side is evaluated:

It doesn't. Both sides (operands) of the list assignment operator evaluated, and whether the list assignment is evaluated in scalar context or list context does not affect the evaluation of the operands whatsoever.

Whether a list assignment is evaluated in scalar context or list context only affects the value it returns.

I have previously created Scalar vs List Assignment Operator, which attempts to make clear the differences between the two assignment operators and how they behave in scalar and list context.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I edited that sentence to clarify a bit: `It seems weird to me that context affects which part of the evaluated expression is returned:` I know that context doesn't affect the evaluation and only the return result, but I want to know how it works. If the right hand side of an assignment is evaluated and then assigned to the left hand side, then why would the scalar get a 2? The right hand side evaluates to a 3 element list, which should then be assigned to the scalar. It's like perl just decides to ignore precedence rules and choose which part of the expression should be returned. – Brian Feb 16 '12 at 20:21
  • Unless, of course, `my @greeting = (($hello, $there, $world) = (7,8))` would give `@greeting` `(7,8)` instead of `($hello, $there, $world)`, now that would make much more sense to me. But if `@greeting` gets `($hello, $there, $world)` and `$greeting` gets the result of `(7,8)` then that doesn't make sense. – Brian Feb 16 '12 at 20:42
  • `$greeting` does not get the result of `(7,8)`. In fact, it's impossible for `$greeting` to get the result of this `(7,8)`, since this `(7,8)` evaluates to a list value, and a list value cannot be assigned to a scalar.Both `@greeting` and `$greeting` get the result of the list assignment, which is either "list of values to which its LHS evaluated" or "numbers of elements to which its RHS evaluated" depending on context. You seem to be implying that it would be better to return something different without giving any reason whatsoever as to how to other results would be useful. – ikegami Feb 16 '12 at 21:32
  • 1
    @ikegami, I think he's saying it would be more consistent if expressions in scalar context returned the number of elements they would return in list context. And he's right that it would be more consistent. It would just be less useful, and Perl aims for utility more than consistency. – cjm Feb 16 '12 at 21:48
  • @cjm, The choice of returned values is based on usefulness. There is no consistency. Each operator returns something different. And that's GOOD. Operators are suppose to do different things. Having each operator behave as if surrounded by `(...)[0]` or `(...)[-1]` when called in scalar context would be downright stupid, yet those are the only choices that would make any sense at all for all operators. – ikegami Feb 16 '12 at 21:53
  • @cjm, As for consistency, list assignment in list context is 100% consistent with with scalar assignment in list context. Both return their LHS as lvalues. To claim there is a lack of consistency is completely false. – ikegami Feb 16 '12 at 21:55
  • If I do: `((5 + 5) + (4 + 4))` the result is 18. In Perl, I thought that scalar and list context affects this value, 18. If you take a look at the previous example: `(($hello, $there, $world) = (7,8))` isn't the result of the expression itself `($hello, $there, $world)`? So scalar and list context would affect this value. If the expression works like it does, then it seems like `((5 + 5) + (4 + 4))` should give 8, not 18, which wouldn't make sense. However, now Darch who commented on my post mentioned that lists return `undef` in scalar (not the number of elements, like an array would). – Brian Feb 17 '12 at 02:55
  • So I'm just going to accept that expressions return different results in scalar and list context, instead of getting bogged down with the details. Also note that I wasn't arguing that it should have been done the way I mentioned it, I agree that the way perl handles it is useful, I was just trying to understand how it works, not why it works that way. – Brian Feb 17 '12 at 02:56
  • @Brian, Perfect example. Do you think `$x = ((5+6)*(7+8))` puts `11` or `15` into `$x`? Then why do you think `$x = (($hello, $there, $world) = (7,8))` must put `($hello, $there, $world)` or `(7,8)` into `$x`? (Note that it's not possible to do either!) – ikegami Feb 17 '12 at 05:07
  • @Brian, Lists don't return undef in scalar context, they return their last element evaluated in scalar context. e.g. `$x = (4,5,6);` puts 6 in `$x`. Darch said that particular list would return undef in scalar context, but that has nothing to do with your question. – ikegami Feb 17 '12 at 05:14
  • @Brian, Example of expressions returning different results in scalar and list context: `say(scalar(@a)); say(@a);` (number of elements in `@a` vs the number of elements in `@a`), `say(scalar(4,5,6)); say(4,5,6);` (last element of list evaluated in sclar context vs all elements of the list evaluated in list context), etc – ikegami Feb 17 '12 at 05:24
  • "isn't the result of the expression itself ($hello, $there, $world) ?" -- no, not in scalar context; as ikegami pointed out, that's impossible because a list is not a scalar. You're assuming that scalar context is a matter of producing a list and then converting it to a scalar, but perl isn't designed that way -- scalar context produces a useful value. "I was just trying to understand how it works, not why it works that way. " -- this is an incoherent distinction. It works that way because it's defined to work that way. There's no "how", other than staring that the perl code. – Jim Balter Oct 09 '18 at 19:20
  • "It's like perl just decides to ignore precedence rules and choose which part of the expression should be returned." I think the point @Brian was trying to make is that... if you think that an operator returns some result and only then that result is evaluated in list or scalar context (evaluating a result of performing an operation is what comes after performing the operation), then it looks like perl deifies the laws of... operations :) But AFAICT that's not the case with perl. Returning one or another result is part of performing an operation (think `wantarray ? ... : ...`)... – x-yuri Sep 14 '22 at 08:39
  • ...Or actually if the assignment operator is a special case in one sense or another (handled in a special way)... I wouldn't be surprised. Anyways, it seems Jim Balter understood the question correctly. – x-yuri Sep 14 '22 at 08:41
  • @x-yuri It's special in the sense that `=` can be the list assignment operator or the scalar assignment operator, which is determined by peaking at code on the left of the `=`. But once the operator is chosen (at compile-time), there's nothing special. The context in which both operands are evaluated is known, and what the operator returns in both context is known. – ikegami Sep 14 '22 at 14:02
1

Why does it evaluate the right hand side and produce 2, instead of the newly defined list being evaluated and returning 3?

Because that's what the language definition says it should do, because that's what Larry Wall decided it should do.

To me, it seems like $hello gets 7, $there gets 8, and $world gets undef, then that list is evaluated in scalar context

No, perl does not work that way. You're assuming that scalar context is just a conversion of the result that would have been obtained in list context to a scalar by counting the number of elements in the list result, but that's simply not right ... scalar context means that an operation (in this case assignment) should produce a scalar result, and there are often scalar results that are more useful than just the number of elements in the list context result, and that varies from operator to operator ... you have to check the documentation for what scalar produces; it's not uniform. For list assignment, the scalar value is the number of elements on the right side, because that's vastly more useful than the number of receivers, which is constant. e.g., if (($a, $b) = $s =~ /$re/) is true if the match produced results, as opposed to always being true because there are two receivers.

It seems weird to me that context affects which part of the evaluated expression is returned

There are far weirder things in perl. That feeling of weirdness comes from a mismatch between models ... in this case your expectation of orthogonality and your belief that "scalar context" is an operator on lists, vs. perl's pragmatic design and the reality that "scalar context" establishes a context which determines what result to produce. Note that you can write your own functions that produce completely different results depending on the context of the call.

Jim Balter
  • 16,163
  • 3
  • 43
  • 66