Effective Perl Programming – write better, more idiomatic Perl

Understand why you probably don’t need prototypes

You should understand how Perl’s prototypes work, not so you’ll use them but so you won’t be tempted to use them. Although prototypes can solve some problems, they don’t solve the problems most people want.

Some languages, such as C, have function prototypes. You tell your function how many arguments it has and what sort they are, as well as what type of thing it returns:

Aside from syntax-warping modules, such as Devel::Declare, or source filters, such as Filter::Simple, Perl doesn’t have that as a fundamental feature. It’s subroutines and methods take lists and return lists (or a single item).

Perl does have prototypes as a compile-time aid, documented in perlsub. It’s not there to ensure you give a subroutines particular sorts of arguments but to help the compiler figure out what you typed and how you want it to interpret it. Perl doesn’t require you to surround your arguments in parentheses, so prototypes gives you a way to tell the compiler where the arguments start and end. Consider these examples, which you’ll understand by the end of this Item:

A lesser part of that includes the specification of Perl core types (scalar, array, hash, subroutine, or globs). Method calls, which require the parentheses to surround their arguments, completely ignore prototypes because Perl doesn’t need any help to parse them.

You (optionally) specify the prototype after the subroutine name. The simplest prototype is the empty prototype, meaning the subroutine takes no arguments:

use utf8;
sub TRUE () { 1 }
sub FALSE () { 0 }
sub π () { 3.1415926 }

The simplest prototype

The empty prototype tells perl not to consume any arguments when it sees that function name. How does perl know how to interpret this?

use utf8;
say π +1;

Since π is a subroutine, it can take arguments. Since you wrote it without parentheses, perl needs a hint to parse that. It could be two forms, each with possibly different answers:

use utf8;
say π( +1 );
say π() + 1;

The empty prototype tells perl to parse it as π() + 1. This makes the empty prototype a way that you can declare constants.

This means, however, that perl needs to know about the prototype before it parses that bit of code. This works because the prototype shows up first because the subroutine is completely defined before it’s called:

use utf8;
sub π () { 3.1415926 }
say π +1; # 4.1415926

You don’t need to define the subroutine ahead of time, but you have to declare its prototype to get the behavior that you expect:

The prototype matters only for the calls to the subroutines after its definition. The prototype doesn’t matter for subroutine calls before its definition. However, to use the subroutine as a bareword, you still have to have a forward declaration:

The @array is a single argument, the first one, and is taken in scalar context, giving the number of elements in it. The %hash is treated in the same way, providing the mostly useless “hash statistics” scalar value:

3 2
19/32 2

Putting a \ in front of a prototype character specifies that the argument is a named variable. Instead of the value, you get a reference to the value:

If you wanted to keep the array together, you would put a backslash in front of the \@. The argument must be a named array, and not an anonymous array or a reference to an array. Even though the argument is an array, the value in @_ will be a reference to that array:

Even though you can specify the variable type with the backslashed form, you can’t specify anything about the values that they hold, or limits to the number of elements they contain.

You can also specify prototypes for subroutines and globs, which we’ll cover in a separate Item since you can have a lot more fun with those.

Optional arguments

So far, you’ve used prototypes that specify an exact number of elements. If you want to specify optional arguments, you can divide the mandatory and optional prototype characters with a semicolon. If you wanted to take at least two but possible three arguments, you’d use the prototype ($$;$)

A final warning

Subroutine prototypes exist chiefly so perl can parse calls to your subroutines just like it would its built-ins—without parentheses. They can set the context for the arguments or the variable types, but they can’t specify the sorts of values. Prototypes aren’t the tools that you want if any of those are your goal. It’s also easier to just use parentheses to mark your argument list.

Things to remember

Prototypes are not function signatures

Non-backslashed prototype characters enforce a context, not a type

Backslashed prototype characters enforce a variable type

You can specify optional arguments after a semicolon in the prototype.