Scalar Context: Lists Versus Arrays
For a long time after I first encountered Perl, I looked on "list" and "array" as essentially interchangeable concepts. A list was simply the source construct corresponding to an array. This idea is mostly correct. But as they say, the devil is in the details.
One of the differences is what happens to them in scalar context. An array evaluates to the number of elements it contains. A list evaluates to its last element. So:
my @array = qw{ one two five }; say scalar @array; # prints '3' { no warnings 'void'; # Note the need for this say scalar( qw{ one two five } ); # prints 'five' }
Okay, that is a trivial example. It becomes more interesting when you consider that subroutines inherit their calling context. If called in scalar context, a subroutine that returns a list behaves differently than one that returns an array:
sub array { state $array = [ qw{ one two five } ]; return @{ $array }; } sub list { return qw{ one two five }; } say scalar array(); # prints 3 say scalar list(); # prints 'five';
Now, there is some sentiment against subroutines that "behave differently" in scalar and list context. Usually this is thought of in terms of the wantarray()
built-in, and there is actually Perl Critic policy Perl::Critic::Community::WantArray
to flag these.
But it seems to me that any Perl subroutine that returns more than one value will behave differently in scalar context: it's just a question of whether you want the array behavior, the list behavior, or the arbitrary behavior you can get with wantarray()
. The difference between good code and bad code is a matter of choosing this behavior carefully.
P.S.
What do you do if you have an array but want list behavior? There is no list
built-in corresponding to the scalar
built-in. The documentation for scalar
talks about this, but only addresses interpolation. In the general case, though, what seems to work is slicing the entire array:
say scalar @array[ 0 .. $#array ]; # prints 'five'
Or, if you want to encapsulate this behavior,
sub make_list { return @_[0..$#_] } say scalar make_list( qw{ one two five } ); # prints 'five';
No, I did not come up with this on my own. I got it from Stack Overflow, specifically from user2404501
's response.
Be careful of getting too fancy with this. scalar @array[ 0 .. $#array ]
is written much more clearly as $array[-1]
.
Not only is that clearer, it’s also (potentially far) faster. So there might be some room for something like
for use in a function whose return expression is in fact just an array variable (rather than a more complex expression with array return behaviour, like a map or grep). But even then, you’ll also want to check
wantarray
earlier on in the function to avoid constructing the entire array in the case where you’re only going to return the last element anyway. So on the callee side, when it comes to controlling the return value of a function, you really want awantarray
expression.That leaves the case on the caller side, where you call a function which has array return behaviour but you want list return behaviour. That only happens in an expression where you need a scalar. That means the nonexistent list operator exists: it is just spelled
+( ... )[-1]
. But… that is still kind of a silly thing to do, for the same reason as on the callee side (obviously): you are throwing away all the computation (and memory allocation and other effort) that went into constructing most of the return value. You should be rearranging the code to not have to do this in the first place, if you can at all do so. (E.g. instead of+( sort ... )[-1]
you really wantList::Util::minstr( ... )
.)And that, overall, I think, is why there is no list operator.
I just interesting about why we have this feature, was it useful in old time?
Quite right. The only reason I can think of for not short-circuiting in scalar context is if the extra data returned in list context need to be computed anyway.
I do not know about old time usefulness. If it was, it was before I started using Perl, and that was at about version 5.6.
Honestly, I think of this list behavior as Perl weirdness, and have no idea why Larry Wall had Perl act this way. It does make lists more like blocks (which also return their last-computed value), but I do not know whether that is the reason for this behavior.
Well written! Thanks.
There's also the perlfaq4 answer to "What is the difference between a list and an array?" https://perldoc.perl.org/perlfaq4#What-is-the-difference-between-a-list-and-an-array?