Why I want a new prototype in the perl core

Most of the time, you shouldn't use Perl's function prototypes. They aren't function signatures the way you expect. Instead, they mostly help the parser understand how to interpret arguments using the right context.

Here is a simple example:

sub foo($) { say shift }

The '$' prototype says that foo takes a single argument in scalar context. Consider this:

$ perl -wE 'sub foo($){say shift} @array=("a".."z"); foo(@array)'
26

If your reaction is, "What?!?", you need to remember than an array in scalar context is the length of the array. And just to drive home the point that prototypes can be surprising, consider these two:

$ perl -wE 'sub foo($){say shift} foo("a","b","c")'
Too many arguments for main::foo at -e line 1, near ""c")"
Execution of -e aborted due to compilation errors.

$ perl -wE 'sub foo($){say shift} foo(qw/a b c/)'
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.
c

In the former case, the error is clear. In the latter case, the qw/a b c/ is equivalent to a parenthesized expression ("a", "b", "c") which in scalar context evaluates throws away the left values and returns the right-most argument. (See documentation for the Comma Operator.)

These kinds of unexpected errors are why you generally shouldn't use prototypes. However, they are absolutely necessary to override or mimic the effect of a built-in keyword.

For my work on push() and keys(), I want to be able to represent a prototype that takes an array or an array reference or a hash or a hash reference. You can get perl to pass a reference to an array instead of the array itself using the \@ prototype like this:

$ perl -wE 'sub foo(\@){say shift} @array=("a".."z"); foo(@array)'
ARRAY(0x1156588)

But that doesn't let you use an array reference as an argument -- only a literal array variable can be used, even though a reference is what is passed to the function when called as foo(@array).

$ perl -wE 'sub foo(\@){say shift} @array=("a".."z"); foo(\@array)'
Type of arg 1 to main::foo must be array (not reference constructor) at -e line 1 ...

$ perl -wE 'sub foo(\@){say shift} foo(["a".."z"])'
Type of arg 1 to main::foo must be array (not anonymous list ([])) at -e line 1 ...

Since the prototype I want doesn't exist, I've started working on a patch that would allow something like this to work:

sub foo(+) { say shift }

foo( @array );   # passed as \@array
foo( %hash );    # passed as \%hash
foo( $arrayref );
foo( $hashref );
foo( bar() );    # bar() called in scalar context

The "+" prototype passes literal array or hash variables using a reference, but passes any other argument in scalar context. I call it a "single term" prototype. With luck and favorable reviews, I hope it will make it into the November development release of Perl.

This entry was posted in p5p, perl programming and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

4 Comments

  1. Posted October 19, 2010 at 8:00 pm | Permalink

    You can use \[$@] prototype to handle some of the cases.

    use 5.010;
    sub foo(\[$@]){my $ar=shift;$ar=$$ar if ref($ar) eq 'REF';say $ar;}
    $ar=\@array;
    foo($ar);foo(@array);

    But it will not work in case of

    foo(['a'..'z']);

  2. Posted October 19, 2010 at 11:20 pm | Permalink

    That's exactly the issue. \[] is too narrow because it requires an actual variable. Plus, it's not enough to check ref($ar) eq 'REF', but also that ref($$ar) eq 'ARRAY'. There is an efficiency loss in the extra referencing and dereferencing, which the new prototype will eliminate.

  3. Jay
    Posted October 20, 2010 at 5:34 am | Permalink

    sub foo(\@) { say shift; }
    my @array = 1..15;
    foo(@array); foo(@{[ 1..10 ]});

    '+' is a pretty bad choice for a new symbol, but perhaps you could make it a modifer (like \), so sub foo(+@) or sub foo(+%)

  4. Posted October 20, 2010 at 6:56 am | Permalink

    I feel your pain about another symbol. Someone on #p5p referenced Nethack. Unfortunately, the implementation of prototypes doesn't make that an easy change, whereas adding a symbol for this weaker case of '$' is actually quite straightforward.

© 2009-2014 David Golden All Rights Reserved