Why I want a new prototype in the perl core

Reading time: 3 minutes

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.

•      •      •

If you enjoyed this or have feedback, please let me know by or