Closures
Part 1: WHAT THE HECK IS A CLOSURE?
This page explains what a closure is in Perl and how to use them. A closure is a function that captures its lexical environment, which just means it remembers the variables that existed when it was created. Those variables stay alive even after the outer function finishes running. The code block below shows the standard 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 captures the variables themselves rather than just their current values. Each variable lives on and stays bound to that specific subroutine. This behavior matters because it gives you state preservation without objects.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 routine that builds and returns other functions. Closures make this pattern possible.The factory pattern is powerful because you parameterize behavior at creation time and then call the result like any other 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 capture lexical variables declared withmy. Package variables do not get captured because they live in the global scope.
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 work 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 pattern trips up almost everyone at least once.Wait, that works? Yes, because Perl 5.10+ creates a fresh $i for each iteration when you usemy @closures; for my $i (1..3) { push @closures, sub { say $i }; } $_->() for @closures; # 1, 2, 3 - works!
my in the loop declaration.
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 is 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 is 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.
perl.gg"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.