spirit_kijkt

Chris Cigno recently wondered why the following subroutine kept printing ’1′:

    #-------------------
    sub readarg {
    #-------------------
    my $type = @_;
    print $type;
    }


Jay Baldwin came back with the correct fix:

    my ($type) = @_;

I did not want to bother most of the readers of the list with more details on why the second form works correctly and why the first one fails. But this blog is the perfect place to do so.

What is returned by an array (the @-thing) depends on the context.
Every Perl expression is evaluated in one of two `contexts’, either list context or scalar context, depending on whether it is expected to produce a list or a scalar. Many expressions have quite different behaviors in list context than they do in scalar context.

Suppose you have a Perl EXPRESSION, you can put that EXPRESSION in list context:

    @array = EXPRESSION;

or you can put it in scalar context, by saying:

    $scalar = EXPRESSION;

One of the expressions that behaves differently depending on the context is the array:

    @array1

When put in list context it will simply produce a list of the items that are on the array @array1.

    @array2 = @array1;

The result will be that @array2 will be set equal to the list of items, so it will become a copy of @array1.
But when you evaluate things in scalar context, the @array1 expression will return the number of items on the array @array1.

    $var = @array1;

Which means that in this case $var will be set equal to the number of items on the array @array1.

Lets take a look at some more examples:

    my @array1 = ('alpha', 'beta', 'gamma');
    my @array2 = @array1;
    my ($first, $second, $third) = @array2

Well if you see it written out like that it becomes simple, doesn’t it?
The first line defines @array1 to contain 3 items (lists are always written as things within parenthesis and seperated by comma’s).
Line 2 copies the complete @array1 into array @array2 (remember list context).
And the last line evaluates @array2 again in list context, so it returns a list of 3 items. And this list goes nicely into the new list that we have on the left side of the expression ($first, $second, $third).
The result will be that $first is set to ‘alpha’, $second to ‘beta’ and $third to ‘gamma’.

So what will happen when we change the last line into:

    my ($first, $second) = @array2;

Well @array2 still returns a list with 3 items, but on the left side we have only 2 items waiting. Nothing bad happens. $first will still contain ‘alpha’ and $second will contain ‘beta’. And one could say that ‘gamma’ goes to waste as it does not ‘fit’ in the list on the left side of the expression.

And what happens if we have too many elements on the left side?

    my ($first, $second, $third, $four) = @array2;

In this case the 3 elements returned by @array2 in list context will set $first, $second and $third. $fourth will not get set as the is no 4th element returned and will be undefined.

Now the @_ that Chris was using is just an array like any other array, just that it contains the list of arguments that get passed to the subroutine. Chris called the subroutine in the macro or tag expansion as follows:

    <mymacro;12p36;56p898;122p087;39p079>
    expansion:
    </Pb;;myperl.pl;XPP::readarg(q/$*/)>

So actually Perl saw this call as:

    readarg('12p36;56p898;122p087;39p079');

The list of arguments to the subroutine readarg contains only 1 item, a string containing ’12p36;56p898;122p087;39p079′.

And subsequently when you do:

    my $type = @_;

you evalute the array @_ in scalar context, so it returns you the number of items, which would be in this case always 1.

But when you do the correct thing:

    my ($type) = @_;

the one item returned by the list of arguments @_ will set the $type variable, because the parenthesis around $type define a list with 1 item and forced @_ to be evaluated in list context. (just like in the ($first, $second, $third) example).

Perl would not be Perl if there would not be another way to set $type:

    my $type = shift @_;