Subroutines
๐ง Subroutines
๐ Defining and Calling a Subroutine
sub greet { say "Hello!"; } greet(); # Hello!
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
sub add { my ($a, $b) = @_; # unpack @_ into named scalars โ the standard idiom return $a + $b; } say add(3, 4); # 7
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
sub greet_user { my ($name, $greeting) = @_; $greeting //= "Hello"; # //= โ assign only if $greeting is undef say "$greeting, $name!"; } greet_user("Ada"); # Hello, Ada! โ $greeting was never passed, so it's undef, then defaulted greet_user("Ada", "Hi"); # Hi, Ada! say scalar(@_); # inside any sub, tells you exactly how many arguments were passed
//= ("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
sub square { my ($n) = @_; return $n * $n; } sub square_implicit { my ($n) = @_; $n * $n; # no explicit return โ the last evaluated expression is returned automatically }
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().
sub get_scores { my @scores = (85, 92, 78); if (wantarray()) { return @scores; # called in LIST context โ return every score } else { return scalar(@scores); # called in SCALAR context โ return just the count } } my @all = get_scores(); # (85, 92, 78) โ list context my $count = get_scores(); # 3 โ scalar context
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)
sub double_it { $_[0] *= 2; # modifies the CALLER's original variable, not a local copy! } my $x = 5; double_it($x); say $x; # 10 โ $x itself changed, even though it wasn't explicitly passed "by reference"
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
sub format_price { my ($amount, $currency) = @_; $currency //= "USD"; return sprintf("%.2f %s", $amount, $currency); } say format_price(19.9); # 19.90 USD say format_price(19.9, "EUR"); # 19.90 EUR
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.