In perl, what does a parenthesized list of '$' mean in a sub declaration?

Viewed 146

I have to debug someone else's code and ran across sub declarations that look like this...

sub mysub($$$$) {
    <code here>
}

...also...

sub mysub($$$;$) {
    <code here>
}

What does the parenthesized list of '$' (with optional ';') mean? I ran an experiment and it doesn't seem to care if I pass more and fewer args to a sub declared this way than there are '$' in the list. I was thinking that it might be used to disambiguate two different subs with the same name, differring only by the number of args pased to it (as defined by the ($$$$) vs ($$$) vs ($$) etc... ). But that doesn't seem to be it.

2 Answers

That's a Perl subroutine prototype. It's an old-school way of letting the parser know how many arguments to demand. Unless you know what they are going to do for you, I suggest you avoid these for any new code. If you can avoid prototypes, avoid it. It doesn't gain you as much as you think. There's a newer but experimental way to do it better.

The elements after the ; are optional arguments. So, mysub($$$$) has four mandatory arguments, and mysub($$$;$) has three mandatory arguments and one optional argument.

A little about parsing

Perl lets you be a bit loose about parentheses when you want to specify arguments, so these are the same:

print "Hello World";
print( "Hello World\n" );

This is one of Perl's philosophical points. When we can omit boilerplate, we should be able to.

Also, Perl lets you pass as many arguments as you like to a subroutine and you don't have to say anything about parameters ahead of time:

sub some_sub { ... }
some_sub( 1, 2, 3, 4 );
some_sub 1, 2, 3, 4;   # same

This is another foundational idea of Perl: we have scalars and lists. Many things work on a list, and we don't care what's in it or how many elements it has.

But, some builtins take a definite number of arguments. The sin takes exactly one argument (but print takes zero to effectively infinity):

print sin 5, 'a'; # -0.958924274663138a  (a is from `a`)

The rand takes zero or one:

print rand;    # 0.331390818188996
print rand 10; # 4.23956650382937

But then, you can define your own subroutines. Prototypes are a way to mimic that same behavior you see in the builtins (which I think is kinda cool but also not as motivating for production situations).

I tend to use parens in argument lists because I find it's easier for people to see what I intend (although not always with print, I guess):

print sin(5), 'a';

There's one interesting use of prototypes that I like. You can make your own syntax that works like map and grep block forms:

map { ... } @array;

If you want to play around with that (but still not subject maintenance programmers to it), check out Object::Iterate for a demonstration of it.

Experimental signatures

Perl v5.20 introduced an experimental signatures feature where you can give names to parameters. All of these are required:

use v5.20;
use feature qw(signatures);
sub mysub ( $name, $address, $phone ) { ... }

If you wanted an optional parameter, you can give it a default value:

sub mysub ( $name, $address, $phone = undef ) { ... }

Since this is an experimental feature, it warns whenever you use it. You can turn it off though:

no warnings qw(experimental::signatures);

This is interesting.

I ran an experiment and it doesn't seem to care if I pass more and fewer args to a sub declared this way than there are '$' in the list.

Because, of course, that's exactly what the code's author was trying to enforce.

There are two ways to circumvent the parameter counting that prototypes are supposed to enforce.

  1. Call the subroutine as a method on an object ($my_obj->my_sub(...)) or on a class (MyClass->my_sub(...)).
  2. Call the subroutine using the "old-style" ampersand syntax (&my_sub(...)).

From which we learn:

  1. Don't use prototypes on subroutines that are intended to be used as methods.
  2. Don't use the ampersand syntax for calling subroutines.
Related