Skip to content
VLSI Mentor

Perl Part I · Module 10 · Subroutines and Arguments

Signatures and Prototypes

Everything so far in this module has unpacked arguments by hand, because that is what Perl required for most of its history and what almost all existing code does. Newer Perl lets you name the parameters in the definition instead, with defaults, and gives you the one thing hand-unpacking cannot: an error when the caller passes the wrong number of arguments. There is also an older feature whose syntax looks almost identical, which does something else entirely — it changes how calls to the subroutine are parsed, checks nothing about types, and is one of the more misunderstood corners of the language. Knowing which one you are looking at matters, because they appear in the same position and mean different things.

Intermediate15 min readPerlPart ISubroutines

Part I · Module 10 · Chapter 10.11 · Signatures and Prototypes

1. Signatures Name the Parameters

Azvya Education Pvt. Ltd.VLSI Mentor
sig-basic.pl
use strict;
use warnings;
use feature "signatures";

sub add ($a, $b) { return $a + $b }

printf "add(2, 3) = %d\n", add(2, 3);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
add(2, 3) = 5
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
The signatures feature is experimental at sig-basic.pl line 5.
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
0

The parameters are named in the definition, there is no my ($a, $b) = @_; line, and it works — with a warning on every compile.

That warning is the headline fact about signatures on Perl 5.34, which is the version used throughout this module. The feature is usable and marked experimental, which has two practical consequences covered in §4.

Silencing the warning is per-category, and defaults work:

Azvya Education Pvt. Ltd.VLSI Mentor
sig-quiet.pl
use strict;
use warnings;
use feature "signatures";
no warnings "experimental::signatures";

sub add ($a, $b = 10) { return $a + $b }

printf "add(2, 3) = %d\n", add(2, 3);
printf "add(2)    = %d\n", add(2);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
add(2, 3) = 5
add(2)    = 12

$b = 10 is a default in the declaration — the equivalent of Chapter 10.3 §2's $b //= 10; but stated where a reader looks for the interface. A default may be any expression, and it is evaluated only when the argument is absent.

2. The One Thing @_ Cannot Do

3. Slurpy Parameters

A signature can end with an array or a hash, which collects whatever is left.

Azvya Education Pvt. Ltd.VLSI Mentor
sig-slurpy.pl
use strict;
use warnings;
use feature "signatures";
no warnings "experimental::signatures";

sub first_and_rest ($first, @rest)  { return "$first + " . scalar(@rest) . " more" }
sub named_rest     ($first, %opt)   { return "$first with " . join(",", map { "$_=$opt{$_}" } sort keys %opt) }

print first_and_rest(1, 2, 3, 4), "\n";
print named_rest("alu", corner => "ss", limit => 5), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
1 + 3 more
alu with corner=ss,limit=5

Two useful shapes:

  • ($first, @rest) is the variadic form — one required argument and any number after it. The arity check applies only to the named parameters, so @rest may be empty.
  • ($first, %opt) makes Chapter 10.5's pattern declarative. The named arguments arrive as a hash without a my %args = @_; line, and the required positional argument is checked.

That second form is genuinely nicer than the hand-written version, with one caveat: the defaults still have to be applied in the body, because a signature default applies to a scalar parameter and not to keys inside a slurpy hash:

Azvya Education Pvt. Ltd.VLSI Mentor
Named arguments with a signature still need their defaults
sub report ($block, %opt) {
    $opt{corner} //= "ss";
    $opt{limit}  //= 0;
    ...
}

So the signature buys the required-argument check and loses nothing; the defaults line is the same as Chapter 10.3's.

An odd number of named arguments behaves better than the hand-written version, which is worth measuring because it is easy to assume otherwise:

Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR, calling ($blk, %opt) with an odd trailing count
Odd name/value argument for subroutine 'main::r' at -e line 3.
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
255

That is a different message and a fatal error. Chapter 10.5 §4 measured the hand-written my %args = @_; form giving Odd number of elements in hash assignment as a warning, with the hash still built and exit 0 — and with the line number pointing inside the subroutine rather than at the caller.

The signature version names the subroutine, refuses to run, and exits non-zero. So for named arguments a slurpy-hash signature gives you two checks the hand-written form does not: the required positional argument, and a fatal odd-count error.

4. Should You Use Them?

An honest answer, because this is the chapter's real question.

What "experimental" means here. The warning is not a prediction that signatures will be removed — they have been in Perl since 5.20, were used widely before 5.36 made them stable, and the design has settled. What it means in practice is:

  • Every compile prints a warning unless you add no warnings "experimental::signatures", which is a line of noise in every file that uses them.
  • Details have changed between versions. The exact spelling of what is allowed in a signature has moved over the 5.20–5.36 range, so a signature written against one Perl may not behave identically on an older one.
  • They require a use feature line. A file using signatures will not compile at all on a Perl older than 5.20, which for an EDA environment running a vendor-supplied Perl is a real constraint.

The recommendation, and why the curriculum marks this chapter Medium:

SituationUse
a script only you run, on a Perl you controlsignatures, with the no warnings line
a script that may run on an old vendor Perlhand-unpacking
adding to an existing codebasewhatever that codebase already uses
a shared library with many callershand-unpacking plus an explicit count check

The deciding question is the Perl version you cannot choose. Flow scripts in this trade frequently run under whatever Perl a tool ships with, and perl -v on a vendor installation is not always recent. Chapter 1.2 covers finding that out, and it is worth doing before committing a file to signatures.

What this means for reading: hand-unpacking is what you will mostly see, which is why Chapter 10.3 is Foundation and this chapter is Medium. Signatures are worth recognising and worth using where you can.

5. Prototypes Are Not Signatures

The syntax collides, and the meanings do not overlap at all.

Azvya Education Pvt. Ltd.VLSI Mentor
proto-shape.pl
use strict;
use warnings;

sub pr ($$) { return "@_" }

print "pr('a','b') = ", pr("a", "b"), "\n";

my $ok = eval q{ pr(1); 1 };
print $ok ? "pr(1) compiled\n" : "pr(1) failed to compile\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
pr('a','b') = a b
pr(1) failed to compile

sub pr ($$) without use feature "signatures" is a prototype, not a parameter list. $$ does not mean "two scalars called $ and $" — it means "this subroutine takes exactly two scalar-context arguments", and Perl enforces that when it compiles each call.

The clearest demonstration is passing an array:

Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR, calling a ($$) prototype with an array
Not enough arguments for main::p at -e line 1, near "@a)"
Execution of -e aborted due to compilation errors.
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
255

A two-element array is not two arguments to a prototyped subroutine. The prototype imposed scalar context on each expected argument, so @a counted as one, and the whole program failed to compile.

That is the key difference, and it has three parts:

SignaturePrototype
needs use featureyesno
names parametersyesno — $$ names nothing
checks the countat run time, fatallyat compile time
changes how calls parsenoyes — imposes context
checks typesnono
@_ still populatedyesyes

Neither checks types. The $ in a prototype means "scalar context", not "must be a number" or "must be a string". A prototype of ($$) accepts pr("a", {}) happily.

What prototypes exist for is making a subroutine parse like a builtin:

Azvya Education Pvt. Ltd.VLSI Mentor
The legitimate use
sub mymax (\@) { my ($aref) = @_; ... }     # takes an array, like push does
mymax(@values);                              # no backslash needed at the call site

That \@ prototype makes Perl pass a reference automatically, which is how push can take @array as its first argument without a \. It is a genuine feature and a rare need.

6. Why This Matters in Engineering Work

The arity check is the one real gap signatures fill. Chapter 10.2 §4 measured that an extra argument is silently discarded, which is the most invisible of the argument mistakes. A signature turns it into a message naming both counts — and more importantly, turns "I added a parameter" from a silent behaviour change into a list of call sites to fix.

The Perl version is often not yours to choose. A script that must run under a tool's bundled Perl cannot use use feature "signatures" if that Perl predates 5.20, and finding out is one perl -v away. That constraint, not the language design, is why hand-unpacking remains the default in this trade.

And a prototype found in inherited code is worth reading carefully. It changes how calls parse, is bypassed by & and by code references, and applies only below the declaration. A subroutine whose behaviour depends on how it was called is hard to reason about, and a prototype is the one feature in this module that creates that situation on purpose.

7. Common Mistakes

Reading sub f ($$) as a parameter list. Without use feature "signatures" it is a prototype and names nothing.

Expecting a prototype to check types. Neither feature does. $ means scalar context.

Passing an array to a ($$) prototype. It counts as one argument and the program fails to compile.

Expecting a signature's arity error at compile time. It is a runtime error, catchable with eval.

Forgetting no warnings "experimental::signatures" on 5.34. Every compile warns.

Using signatures in a script that must run on a vendor Perl. Check perl -v first; below 5.20 the file will not compile.

Expecting a signature default to apply to slurpy hash keys. Defaults apply to scalar parameters; key defaults still go in the body. (The odd-count check, though, is fatal rather than a warning.)

Adding a prototype to an ordinary subroutine. It is for emulating builtins, is invisible at the call site, and is bypassed by & and by code references.

8. Exercise

Exercise 1 — a signature. Write sub add ($a, $b) { ... } with use feature "signatures". Record STDOUT, STDERR and the exit status. Then add the no warnings line and record again.

Exercise 2 — a default. Give $b a default of 10 and call the subroutine with two arguments and with one. Record both. Then make the default an expression that prints something, and confirm it is evaluated only when the argument is absent.

Exercise 3 — the arity check. Write a two-parameter signature and call it with one, two and three arguments, catching failures with eval. Record all three messages. Then write the same subroutine with my ($a,$b) = @_; and repeat, recording what happens for each.

Exercise 4 — adding a parameter. Write a two-parameter subroutine with a signature and a caller passing two arguments. Add a third required parameter without touching the caller. Record what happens. Then do the same with hand-unpacking.

Exercise 5 — slurpy. Write ($first, @rest) and ($first, %opt) signatures and call each. Then call the %opt one with an odd number of trailing arguments and record the exact message and exit status. Compare both against the hand-written my %args = @_; version from Chapter 10.5 §4.

Exercise 6 — the version question. Run perl -v and record the version. Say whether a signature-using file would compile there, and what you would check before committing one to a shared flow.

Exercise 7 — a prototype. Write sub pr ($$) { return "@_" } with no use feature line. Call it with two scalars, then with one, then with a two-element array. Record all three outcomes and note which failed at compile time.

Exercise 8 — the bypass. Call that prototyped subroutine three ways with a single argument: directly, as &pr(1), and through a code reference. Record which of the three the prototype rejected.

9. Summary

  • Signatures name parameters in the definition and support defaults: sub add ($a, $b = 10).
  • On Perl 5.34 they work and warn — The signatures feature is experimental on every compile, silenced with no warnings "experimental::signatures".
  • They enforce the argument count, fatally — measured Too few arguments for subroutine 'main::two' (got 1; expected 2) and the matching too-many message. That is the one thing @_ cannot do.
  • So adding a parameter breaks existing callers immediately instead of leaving them passing undef.
  • The arity error is at run time, catchable with eval — a signature is a guaranteed check, not a static one.
  • Slurpy @rest and %opt work, and ($first, %opt) makes Chapter 10.5's pattern declarative — key defaults still go in the body, but an odd name/value count is fatal (Odd name/value argument for subroutine, exit 255) where the hand-written form only warns and continues.
  • The deciding question is the Perl version you cannot choose. Below 5.20 a signature file will not compile at all, which is why hand-unpacking remains the default in this trade.
  • A prototype is a completely different feature. sub pr ($$) without use feature names nothing, checks shape at compile time, and imposes context — measured, a two-element array counted as one argument and the program failed to compile.
  • Neither feature checks types.
  • Prototypes are bypassed by & and by code references, and apply only to calls below the declaration. Use them only to emulate a builtin.

Chapter 10.12 closes the module with the three decisions that make a subroutine worth calling from a second script: what it takes, what it returns, and what it does when the input is wrong.

Where this fits

Part of the Perl curriculum.