Closures & Functional Techniques

Perl Intermediate/Advanced — Closures & Functional Techniques
Perl Intermediate/Advanced
Course 2 · Chapter 7 · Closures & Functional Techniques

🔒 Closures & Functional Techniques

Course 1, Chapter 7 covered named subroutines. This chapter covers anonymous ones — and the genuinely powerful closure mechanism they unlock — alongside map/grep/sort, Perl's core tools for functional-style list processing.

🕶️ Anonymous Subroutines

my $add = sub { my ($a, $b) = @_; return $a + $b; }; say $add->(3, 4); # 7 — calling a coderef uses the arrow, like Course 1, Chapter 9's dereferencing

sub { ... } with no name creates a coderef — a reference to a subroutine, storable in a scalar exactly like the array/hash references from Course 1, Chapter 9. Calling it uses the same -> arrow syntax.

🔐 Closures — Capturing Lexical Variables

An anonymous sub closes over any lexical (my) variables from its surrounding scope — it doesn't just read their value once, it remembers the variable itself, even after the scope that declared it has technically ended:

sub make_counter { my $count = 0; return sub { return ++$count; }; # this anonymous sub closes over $count } my $counter1 = make_counter(); my $counter2 = make_counter(); say $counter1->(); # 1 say $counter1->(); # 2 say $counter2->(); # 1 — a completely independent $count, not shared with $counter1

Each call to make_counter() creates a genuinely fresh $count, and the anonymous sub returned each time closes over that specific variable — $counter1 and $counter2 maintain entirely separate, independent state, even though they were built from the exact same code.

🎯 Practical Uses of Closures

sub make_multiplier { my ($factor) = @_; return sub { my ($n) = @_; return $n * $factor; }; } my $double = make_multiplier(2); my $triple = make_multiplier(3); say $double->(5); # 10 say $triple->(5); # 15

Closures are genuinely useful for: generating a family of related subroutines from one template (as above), private state without building a full class (Chapter 3/4's OOP) for something too simple to warrant it, and passing behavior around as a callback — a subroutine reference handed to another function to be invoked later.

🗺️ map — Transform Every Element

my @numbers = (1, 2, 3, 4); my @squared = map { $_ * $_ } @numbers; say "@squared"; # 1 4 9 16

$_ is the implicit "topic" variable — the same default variable Chapter 6's bare /pattern/ matches against — set to each element of @numbers in turn. map is more idiomatic than a manual foreach/push loop for a pure element-by-element transformation.

🔍 grep — Filter Elements

my @evens = grep { $_ % 2 == 0 } @numbers; say "@evens"; # 2 4

grep keeps only the elements where the block evaluates true — Perl's built-in filtering function, named after the classic Unix command line tool it takes its name from.

🔀 sort with a Custom Block — Revisited

Chapter 3 (Course 1) showed sort { $a <=> $b } @nums for numeric ordering. The same mechanism sorts complex structures by any field:

my @people = ( { name => "Ada", age => 28 }, { name => "Alan", age => 41 }, { name => "Grace", age => 36 }, ); my @by_age = sort { $a->{age} <=> $b->{age} } @people;

Inside sort's block, $a and $b hold two elements being compared — with hashrefs (Course 1, Chapter 9), accessing a specific field with ->{age} before comparing sorts the whole structure by that field.

🔗 Chaining map/grep/sort Together

my @active_names_by_age = map { $_->{name} } sort { $a->{age} <=> $b->{age} } grep { $_->{active} } @people;

Read from the bottom up: grep keeps only active people, sort orders what's left by age, map extracts just the names from the sorted result. This chained pipeline style is genuinely idiomatic Perl — filter, then order, then transform, each stage doing exactly one job.

⚠ Loop Variables and Closures — Less of a Footgun in Perl Than Elsewhere

In some languages, creating closures inside a loop is a classic trap — every closure ends up sharing the same loop variable, all seeing its final value. Perl's foreach my $x (@list) { ... } genuinely creates a fresh lexical $x on every single iteration, so a closure built inside the loop body correctly captures that iteration's own independent variable — the same trap other languages have doesn't naturally occur here, as long as you declare the loop variable with my (the standard form) rather than reusing an outer variable across iterations.

🔧 A Worked Example — Multiplier Factory + Data Pipeline

my $to_percent = make_multiplier(100); my @ratios = (0.25, 0.5, 0.75); my @percentages = map { $to_percent->($_) } @ratios; say "@percentages"; # 25 50 75

This combines both halves of the chapter directly: $to_percent is a closure built from Chapter 7's factory pattern, and map applies it across a whole list in one line — functional composition, Perl-style.

Closures: Perl vs. JavaScript vs. Python

Perl and JavaScript closures behave almost identically — both freely capture and mutate the captured variable, exactly like this chapter's counter example. Python closures are more restrictive by default: a nested function can read an enclosing variable, but reassigning it requires the explicit nonlocal keyword — without it, Python treats the assignment as creating a new local variable instead. Perl needs no such keyword; a closure captures the variable itself, not just its value, the same way JavaScript does.

Anonymous subs

sub { ... } — a coderef, called with ->().

Closures

Capture the actual lexical variable, not just its value — independent per creation.

map / grep

Transform every element / keep only matching elements, using $_.

sort with $a/$b

Custom comparators sort by any field, including inside complex structures.

💻 Coding Challenges

Challenge 1: An Independent Counter Pair

Using this chapter's make_counter, create two separate counters, call the first one three times and the second one once, and print both counters' current values to prove they're independent.

→ Solution

Challenge 2: Filter and Transform

Given my @numbers = (1..10);, use grep to keep only numbers greater than 5, then map to double each remaining number, printing the final list.

→ Solution

Challenge 3: Sort an Array of Hashrefs by Name

Given this chapter's @people array, use sort to order it alphabetically by name (using cmp, Course 1, Chapter 5's string comparison operator), then map to print just the sorted names.

→ Solution

🎯 What's Next

Next chapter: Testing with Test::Moreok/is/like assertions, prove, and Perl's real testing culture.