Search code examples
perlimplicit-conversionweak-typing

implicit-conversion in perl


I am new to Perl, can anyone explain the following scripts for me please:

#!/usr/bin/env perl
use strict;
use warnings;
sub f1($) { my ($v) = @_; print "f1 $v\n"; }
sub f2(@) { my ($v) = @_; print "f2 $v\n"; }
my $s = "ww";
my @a = ("xx", "yy", "zz");
f1 $s; f1 @a; f2 $s; f2 @a;

The output in my computer is :

f1 ww
f1 3
f2 ww
f2 xx     # why!!

Can anyone explain why the fourth output is xx? I thought it should be zz, since when array converts to scalar, it should be the last element of array.


Solution

  • No, with a statement such as:

    my ($v, $foo, $bar) = @_;
    

    $v will be assigned the first value in the @_ array, $foo the second, and so on. This is because the parentheses impose a list context. Any excess values will be ignored, unless one of your variables is an array, in which case it will slurp all remaining values.

    my ($v, @foo, $bar) = @_;   # wrong! $bar will never get any value
    

    $v will get the first value, @foo all the rest. $bar will be undefined.

    You may be thinking of assignment using a list:

    my $v = qw(a b c);
    

    But this is wrong, and will cause an error:

    Useless use of a constant (a) in void context at -e line 1.
    Useless use of a constant (b) in void context at -e line 1.
    

    This is because the LHS using scalar context, it will (more or less) be similar to:

    'a';
    'b';
    my $v = 'c';
    

    You may notice that if we impose list context by putting $v inside parentheses, we get a different result:

    my ($v) = qw(a b c);  # $v is now 'a'
    

    ETA: Regarding prototypes:

    In f1, what you see is that the array is forced into scalar context because the subroutine expects a scalar argument. That is why f1 with an array prints 3 (the size). When the prototype looks for an array, the array remains in default list context, and the assignment is done as per normal (as described above).

    As an extra note: prototypes have a very specific use, to make subroutines behave more like certain built-ins with regard to argument handling. Such as sort { code here } or push @array, $foo.

    If this is not what you are after, you should skip prototypes all together and simply write:

    sub f1 {
    ...
    }
    

    Documentation here