Closures
Part 1: WHAT THE HECK IS A CLOSURE?
A closure is a function that captures its lexical environment. That's the textbook definition, and it's about as helpful as a chocolate teapot.Let's try again: a closure is a function that remembers the variables that existed when it was created, even after those variables would normally be out of scope.
Still abstract? Here's the canonical example:
See what happened there? The anonymous sub we returned "closed over" the $name variable. Even though create_greeter() finished executing (and $name should have been destroyed), the returned subroutine still has access to it.sub create_greeter { my $name = shift; return sub { say "Hello, $name!"; }; } my $greet_alice = create_greeter('Alice'); my $greet_bob = create_greeter('Bob'); $greet_alice->(); # Hello, Alice! $greet_bob->(); # Hello, Bob!
That's a closure. A function bundled with its environment.
Part 2: THE MECHANICS OF CLOSING OVER
When Perl creates an anonymous subroutine, it doesn't just capture the VALUES of lexical variables - it captures the VARIABLES THEMSELVES. The variables live on, bound to that particular subroutine.This matters:
Each call to create_counter() creates a NEW $count variable, and the returned closure captures THAT specific $count. The two counters are completely independent.sub create_counter { my $count = 0; return sub { return ++$count; }; } my $counter_a = create_counter(); my $counter_b = create_counter(); say $counter_a->(); # 1 say $counter_a->(); # 2 say $counter_a->(); # 3 say $counter_b->(); # 1 (separate instance!) say $counter_b->(); # 2
This is state preservation without objects. Private data without classes. Encapsulation through lexical scope.
Part 3: FUNCTION FACTORIES
A function factory is a function that creates and returns other functions. Closures are what make this possible.The factory pattern is absurdly powerful. You're essentially parameterizing behavior at creation time, then using the result as a standalone function.sub make_multiplier { my $factor = shift; return sub { my $n = shift; return $n * $factor; }; } my $double = make_multiplier(2); my $triple = make_multiplier(3); say $double->(5); # 10 say $triple->(5); # 15 say $double->(10); # 20
MORE ELABORATE EXAMPLE:
sub make_validator { my %rules = @_; return sub { my $value = shift; if (exists $rules{min} && $value < $rules{min}) { return (0, "Value must be >= $rules{min}"); } if (exists $rules{max} && $value > $rules{max}) { return (0, "Value must be <= $rules{max}"); } if (exists $rules{match} && $value !~ $rules{match}) { return (0, "Value must match $rules{match}"); } return (1, "OK"); }; } my $age_check = make_validator(min => 0, max => 150); my $zip_check = make_validator(match => qr/^\d{5}$/); my ($ok, $msg) = $age_check->(25); # (1, "OK") my ($ok, $msg) = $age_check->(-5); # (0, "Value must be >= 0") my ($ok, $msg) = $zip_check->("abc"); # (0, "Value must match...")
Part 4: LEXICAL SCOPE - THE MAGIC INGREDIENT
Closures only work with lexical (my) variables. Package variables don't get captured because they're not lexically scoped - they're global.But with lexicals:our $global = "I'm global"; sub bad_closure { return sub { say $global }; } my $fn = bad_closure(); $global = "Changed!"; $fn->(); # "Changed!" - not captured, just accessed globally
This is why Perl's "use strict" is so important. It forces you into lexical scope, which makes closures work properly.sub good_closure { my $local = "I'm captured"; return sub { say $local }; } my $fn = good_closure(); # Even if there was a way to change $local from outside # (there isn't), $fn would still see "I'm captured"
Part 5: FIRST-CLASS FUNCTIONS AND CLOSURES
Closures are possible BECAUSE Perl treats functions as first-class values. You can:STORE THEM:
PASS THEM:my $fn = sub { ... };
RETURN THEM:some_function(sub { ... });
PUT THEM IN DATA STRUCTURES:return sub { ... };
GENERATE THEM DYNAMICALLY:my @funcs = ( sub { say "one" }, sub { say "two" }, sub { say "three" }, ); $_->() for @funcs;
my @multipliers = map { make_multiplier($_) } 1..10; say $multipliers[4]->(10); # 50 (5 * 10)
Part 6: COMMON GOTCHA - THE LOOP VARIABLE TRAP
This bites everyone at least once:Wait, that works? Yes, because Perl 5.10+ creates a fresh $i for each iteration when you use my in the loop declaration.my @closures; for my $i (1..3) { push @closures, sub { say $i }; } $_->() for @closures; # 1, 2, 3 - works!
But watch out for this older pattern:
All three closures captured the SAME $i, and by the time we call them, $i is 3.my @closures; my $i; for $i (1..3) { push @closures, sub { say $i }; } $_->() for @closures; # 3, 3, 3 - oops!
The fix (for old code or when you can't use loop-scoped my):
for my $i (1..3) { my $captured = $i; # fresh lexical each iteration push @closures, sub { say $captured }; }
Part 7: CLOSURES VS OBJECTS
There's a famous saying: "Objects are a poor man's closures. Closures are a poor man's objects."Both are mechanisms for bundling data with behavior. Compare:
The closure version is more concise and arguably more encapsulated - there's no way to access $count except through the provided interface.# Object approach package Counter; sub new { my $class = shift; my $self = { count => 0 }; return bless $self, $class; } sub increment { ++$_[0]->{count} } sub value { $_[0]->{count} } my $c = Counter->new; $c->increment; say $c->value; # Closure approach sub make_counter { my $count = 0; return { increment => sub { ++$count }, value => sub { $count }, }; } my $c = make_counter(); $c->{increment}->(); say $c->{value}->();
Use objects when you need inheritance, introspection, or the full OO toolkit. Use closures when you need lightweight state encapsulation.
Part 8: THE PHILOSOPHICAL BIT
Closures represent a fundamental truth about computation: context matters. A function isn't just its code - it's its code PLUS the environment in which it was created.This is why closures feel natural for:
- Callbacks (they remember what they were called about)
- Event handlers (they remember their context)
- Partial application (they remember some arguments)
- Memoization (they remember previous results)
- Iterators (they remember where they are)
When you create a closure, you're creating a little world. A self-contained unit of behavior with its own private memories.
That's not just useful. That's beautiful.
"Closures are the poor man's objects, and objects are the poor man's closures." But in Perl, you're never poor. You have both.
Created By: Wildcard Wizard. Copyright 2026