Subroutines
Learn how to define and call subroutines in Perl, pass arguments through @_, and understand return values.
Introduction
As programs grow, repeating the same logic in multiple places becomes error-prone and hard to maintain. Subroutines let you package a block of code under a name, run it whenever you need it, and pass in different data each time. Perl's subroutines are flexible and dynamically typed, with a couple of quirks that are worth understanding early.
- How to define and call a subroutine with sub.
- How arguments arrive inside a subroutine via @_.
- How return works, and what happens when you omit it.
- How to pass named arguments using a hash.
Defining a Subroutine
A subroutine is defined with the sub keyword, a name, and a block of code. You call it later simply by writing its name, optionally followed by parentheses containing arguments.
use strict;use warnings;
sub greet { print "Hello from a subroutine!\n";}
greet();Click Run to see what this code prints.
Arguments via @_
Unlike many languages, Perl subroutines do not declare named parameters in their definition. Instead, every argument passed to a subroutine call lands in the special array @_. It is standard practice to immediately copy the values you need out of @_ into clearly named my variables.
use strict;use warnings;
sub add { my ($a, $b) = @_; return $a + $b;}
my $sum = add(4, 7);print "4 + 7 = $sum\n";Click Run to see what this code prints.
my ($a, $b) = @_; unpacks the first two elements of @_ into $a and $b in one line — this is the idiomatic way nearly every Perl subroutine starts.
Return Values
The return keyword exits a subroutine immediately and hands back a value to the caller. You can return early from inside a conditional, which is a common pattern for guard clauses.
use strict;use warnings;
sub classify_number { my ($n) = @_;
if ($n < 0) { return "negative"; } if ($n == 0) { return "zero"; } return "positive";}
print classify_number(-5), "\n";print classify_number(0), "\n";print classify_number(42), "\n";Click Run to see what this code prints.
Default Return Value
If a subroutine does not explicitly call return, Perl returns the value of the last expression it evaluated. This is convenient for short subroutines, but relying on it in longer ones can hurt readability, so many style guides recommend using return explicitly everywhere.
use strict;use warnings;
sub square { my ($n) = @_; $n * $n; # no explicit return -- this becomes the return value}
my $result = square(6);print "6 squared is $result\n";Click Run to see what this code prints.
Even though Perl allows implicit returns, most style guides recommend always writing return explicitly in subroutines longer than one or two lines, so the intended return value is never ambiguous.
Named Arguments With a Hash
Positional arguments (like add($a, $b)) get confusing once a subroutine takes more than two or three parameters, especially if several are optional. A common Perl idiom is to accept a hash of named arguments instead, which makes call sites self-documenting.
use strict;use warnings;
sub create_user { my %args = @_; my $name = $args{name} // 'Unknown'; my $age = $args{age} // 0;
return "$name ($age years old)";}
print create_user(name => 'Alice', age => 30), "\n";print create_user(name => 'Bob'), "\n";Click Run to see what this code prints.
The // operator above is the defined-or operator: it uses the right-hand value only if the left-hand one is undef, which is perfect for supplying defaults for missing named arguments.
Scope Inside Subroutines
Variables declared with my inside a subroutine are local to that subroutine call and disappear once it returns. Each call gets its own fresh set of my variables, which is essential for subroutines to be safely reusable, including when they call themselves recursively.
use strict;use warnings;
sub factorial { my ($n) = @_; return 1 if $n <= 1; return $n * factorial($n - 1);}
print "5! = ", factorial(5), "\n";Click Run to see what this code prints.
Common Mistakes
- Forgetting to unpack @_ into named variables and referring to $_[0], $_[1] directly, which quickly becomes hard to read.
- Assuming subroutines have named, typed parameters like other languages — Perl does not enforce this at all.
- Relying on the implicit last-expression return in a long subroutine, making it unclear what actually gets returned.
- Forgetting that array or hash arguments are flattened into @_, losing their original shape unless passed by reference.
- Writing a recursive subroutine without a proper base case, causing infinite recursion.
Best Practices
- Always unpack @_ into clearly named my variables at the top of the subroutine.
- Use return explicitly, even in short subroutines, for clarity.
- For subroutines with many or optional parameters, accept a hash of named arguments instead of a long positional list.
- Keep subroutines focused on one task — if a subroutine is doing several unrelated things, split it up.
- Document non-obvious subroutines with a short comment describing expected arguments and return value.
Frequently Asked Questions
Yes — return a list, like return ($sum, $average);, and the caller can unpack it: my ($sum, $avg) = get_stats(@numbers);
Perl does not enforce argument counts. Missing arguments simply appear as undef when unpacked; extra arguments are silently ignored unless the subroutine reads them from @_.
Yes, commonly with the // (defined-or) operator: my $limit = $args{limit} // 10; sets $limit to 10 if it was not supplied.
Pass a reference instead of the array/hash directly, such as process(\@list), which the next lesson on references covers in depth.
Key Takeaways
- sub defines a named, reusable block of code.
- Arguments always arrive in @_; unpack them into named variables right away.
- return exits a subroutine with a value; omitting it returns the last evaluated expression instead.
- A hash of named arguments makes subroutines with many parameters much easier to call correctly.
- my variables inside a subroutine are local to each call, which makes recursion safe.
Summary
Subroutines let you organize and reuse logic cleanly. To build genuinely complex programs, though, you will also need references — Perl's mechanism for pointing at data instead of copying it, which unlocks nested data structures. That is exactly where you are headed next.