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

6 Comments

Not only is that clearer, it’s also (potentially far) faster. So there might be some room for something like

sub list_return (\@) {
wantarray ? @{ $_[0] } : $_[0][-1];
}

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 a wantarray 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 want List::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?

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?

Leave a comment

About Tom Wyant

user-pic I blog about Perl.