Subroutines
🔧 Subroutines
📐 Defining and Calling a Subroutine
You may occasionally see the older &greet(); call syntax in legacy code — it still works, but plain greet(); is the standard modern form and what real code should use.
📦 @_ — Arguments Arrive as a Single Array
Every argument passed to a subroutine — however many there are — lands in the array @_, exactly like Chapter 3's arrays. my ($a, $b) = @_; is a list assignment (also Chapter 3) unpacking the first two elements into named scalars — this line, or a close variant of it, is the first line of nearly every real Perl subroutine.
🔢 Default Values & Variable Argument Counts
//= ("defined-or assign") only assigns if the left side is currently undef — the idiomatic way to supply a default for a missing argument, since a genuinely passed value of 0 or "" (falsy, but defined) is correctly left alone, unlike the similar-looking ||=, which would incorrectly overwrite those too.
↩️ Return Values
Both subroutines above behave identically. Perl, like Ruby, returns the value of the last evaluated expression automatically if no explicit return is reached — genuinely convenient for short subroutines, though an explicit return is usually clearer once a subroutine has more than one possible exit point.
🎭 Context-Sensitive Return — wantarray
This is Chapter 3's list-vs-scalar-context theme taken to its logical extreme: a subroutine can inspect how it was called and return something different accordingly, using wantarray().
wantarray() returns a true value if the subroutine was called in list context, a defined-but-false value in scalar context, and undef in void context (called for its side effects, with the return value discarded entirely) — three genuinely distinguishable answers, not just a boolean.
🔗 Passing Arguments by Reference (Preview)
Unlike most languages, where arguments are copied into the function by default (pass-by-value), Perl's @_ actually aliases the original variables passed in — modifying an element of @_ directly modifies the caller's own variable. In practice this is rarely done deliberately (it's a surprising, easy-to-misuse behavior); the standard idiom of immediately unpacking with my ($a, $b) = @_; sidesteps it entirely, since that copies the values into fresh, independent lexical variables. Chapter 9's references cover the deliberate, explicit way to achieve pass-by-reference behavior when you actually want it.
🔧 A Worked Example
Subroutines: Perl vs. Python
Python's def f(a, b=1, *args, **kwargs) bakes named parameters, defaults, and keyword arguments directly into the language's own function-definition syntax. Perl has none of that built in — every subroutine just receives one flat @_, and named-parameter-style calling is a convention built on top (commonly, passing a hash: configure(name => "Ada", timeout => 30), then unpacking with my %args = @_; inside). More manual, but also more uniform — every subroutine's calling convention is the same flat array underneath, regardless of how the caller chooses to structure the arguments they pass.
@_
Every argument, flattened into one array — unpack with my ($a, $b) = @_;.
Implicit return
The last evaluated expression returns automatically without an explicit return.
wantarray
Detects list vs. scalar vs. void calling context, inside the subroutine itself.
@_ aliasing
Modifying $_[0] directly changes the caller's variable — unpack first to avoid it.
💻 Coding Challenges
Challenge 1: A Subroutine with a Default
Write a subroutine power that takes a number and an optional exponent (defaulting to 2 if not given, using //=), and returns the number raised to that power.
Challenge 2: Context-Sensitive Return
Write a subroutine get_letters that returns the list ('a', 'b', 'c') in list context, and the count (3) in scalar context, using wantarray. Call it both ways and print both results.
Challenge 3: Demonstrate @_ Aliasing
Write a subroutine reset_to_zero that sets $_[0] = 0, call it on a variable holding 99, and show that the original variable changed. Then rewrite the subroutine to safely unpack its argument first (my ($n) = @_;) and show the original variable is no longer affected.
🎯 What's Next
Next chapter: File I/O & Text Processing — open/close, the while (<FH>) idiom, and Perl's original "practical extraction and report" purpose.