Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

perl

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.

like image 242
daveg Avatar asked Jan 25 '23 17:01

daveg


1 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);
like image 154
brian d foy Avatar answered Jan 29 '23 07:01

brian d foy