Home / Alt manpages / perlsub(1)

  • perlsub(1)
  • User command
  • linux

Perl Subroutines: Use Signatures Without Losing @_

You will write and run a small Perl program that passes arguments predictably, returns values in the right context, and avoids accidental changes to the caller's variables. The examples use Perl 5.38.2 on this machine. Allow about 15 minutes if Perl is already installed.

You need a shell, /usr/bin/perl, and permission to create a temporary file in your working directory. No elevated privileges are needed. The manual page is the installed perlsub(1) from the perl-doc package; its behaviour is version-specific where this guide says so.

1. Check the interpreter version

Start by recording the interpreter that will run the examples. Perl syntax and enabled features vary between releases, especially for signatures.

$ perl -v | head -3
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
(with 67 registered patches, see perl -V for more detail)
$ command -v perl
/usr/bin/perl

Checkpoint

If you are using another Perl release, run the examples first and read that release's perlsub documentation before putting signatures into shared code.

2. Start with @_ and copy the arguments

A subroutine without a signature receives one flat list. Its arguments appear in the local @_ array, and the elements are aliases for the caller's scalar arguments. That aliasing is useful for deliberate in-place updates, but it is a common source of surprises.

use strict;
use warnings;

sub add {
    my ($left, $right) = @_;
    return $left + $right;
}

my $total = add(2, 3);
print "total=$total\n";

The assignment to lexical variables copies the scalar values, so add cannot alter its caller through those names.

$ perl add.pl
total=5

Do not assign a whole array to @_ when you intend to update an argument. Replacing @_ removes its aliases. If you need an intentional mutation, make that contract obvious:

sub double_in_place {
    $_[0] *= 2;
}

my $value = 4;
double_in_place($value);
print "value=$value\n";
$ perl mutate.pl
value=8

Safety boundary

Never use an in-place routine with a literal such as double_in_place(4). Perl cannot modify a constant argument; the call can fail at runtime.

3. Replace manual checks with a signature

Signatures give parameters names and enforce their count before the body runs. In Perl 5.38, enable them with use v5.36 or explicitly with use feature 'signatures'. This guide uses the version declaration.

use v5.36;
use strict;
use warnings;

sub add ($left, $right) {
    return $left + $right;
}

print add(2, 3), "\n";
$ perl signed.pl
5

Both parameters are mandatory, and a call with too few or too many arguments throws an exception. That is usually preferable to silently accepting malformed input.

$ perl -e 'use v5.36; sub add ($left, $right) { $left + $right }; print add(2), "\n"'
Too few arguments for subroutine at -e line 1.

A default makes a positional parameter optional. Defaults are evaluated when the call happens, and a default can refer to an earlier parameter.

use v5.36;

sub label ($name, $prefix = "item") {
    return "$prefix:$name";
}

print label("42"), "\n";
print label("42", "record"), "\n";
$ perl defaults.pl
item:42
record:42

Keep the signature and the body close together. A default is not a type check: Perl still performs its normal scalar conversions unless your code validates the value.

4. Test scalar and list return context

Perl subroutines return a flat list of scalars. The caller's context can be scalar, list, or void, and return evaluates its expression in that context. This matters when a routine can sensibly return one summary value or several values.

use strict;
use warnings;

sub words {
    my @words = @_;
    return wantarray ? @words : scalar @words;
}

my @items = words("red", "blue");
my $count = words("red", "blue");
print "list=@items\ncount=$count\n";
$ perl context.pl
list=red blue
count=2

wantarray returns true for list context, false-but-defined for scalar context, and an undefined value for void context. If a function has no useful work to do when its return value is ignored, test defined wantarray before doing that work.

Do not expect arrays or hashes to remain separate when returned together. A return list is flat, just like the incoming argument list. Use references when the caller needs the aggregate boundaries preserved.

5. Pass arrays and hashes by reference

Passing an array directly flattens it into the argument list. Pass a reference when the subroutine needs one array as one argument, or when it may need to change the original deliberately.

use strict;
use warnings;

sub total {
    my ($values) = @_;
    my $sum = 0;
    $sum += $_ for @{$values};
    return $sum;
}

my @values = (4, 7, 9);
print total(\@values), "\n";
$ perl reference.pl
20

\@values creates an array reference and @{$values} dereferences it. The routine above only reads the referenced array. Keep that read-only behaviour when possible; if a routine mutates a referenced structure, document it and test the caller's value afterwards.

6. Diagnose calls and prototypes separately

A prototype is not a signature. Prototypes influence how Perl parses certain calls and can check or reshape arguments at compile time; they do not provide named lexical parameters. Signatures are the feature to use for ordinary parameter declaration.

The explicit ampersand call form, such as &add(2, 3), bypasses prototype checking. It is also possible to write &name without an argument list, which exposes the current @_ to the called routine. That historical shortcut is easy to misread, so prefer an ordinary call or an explicit argument list in new code.

If a subroutine is called before its definition, predeclare it with sub name;, define it earlier, or import it from a module. For a runtime-selected function, use a code reference:

my $operation = sub ($number) { $number * 3 };
print $operation->(7), "\n";
$ perl code-reference.pl
21

If this fails, first check the Perl version and whether the feature declaration is in the same scope as the signature. Then reduce the call to one argument and print the value you are passing. Do not add sudo: subroutine errors are language or program errors, not permission problems.

Done means

  • You confirmed the Perl interpreter and version used for the script.
  • Ordinary parameters are copied from @_ unless mutation is deliberate.
  • Signatures are enabled explicitly and reject missing or extra arguments.
  • You tested scalar and list return context where the result changes by context.
  • Arrays and hashes cross the boundary by reference when their identity matters.
  • You can distinguish a signature from a prototype and an explicit & call.