perl.gg / functional

Closures

2024-07-01

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:

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!
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.

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:

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
Each call to create_counter() creates a NEW $count variable, and the returned closure captures THAT specific $count. The two counters are completely independent.

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.
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
The factory pattern is absurdly powerful. You're essentially parameterizing behavior at creation time, then using the result as a standalone function.

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.
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
But with lexicals:
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"
This is why Perl's "use strict" is so important. It forces you into lexical scope, which makes closures work properly.

Part 5: FIRST-CLASS FUNCTIONS AND CLOSURES

Closures are possible BECAUSE Perl treats functions as first-class values. You can:

STORE THEM:

my $fn = sub { ... };
PASS THEM:
some_function(sub { ... });
RETURN THEM:
return sub { ... };
PUT THEM IN DATA STRUCTURES:
my @funcs = ( sub { say "one" }, sub { say "two" }, sub { say "three" }, ); $_->() for @funcs;
GENERATE THEM DYNAMICALLY:
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:
my @closures; for my $i (1..3) { push @closures, sub { say $i }; } $_->() for @closures; # 1, 2, 3 - works!
Wait, that works? Yes, because Perl 5.10+ creates a fresh $i for each iteration when you use my in the loop declaration.

But watch out for this older pattern:

my @closures; my $i; for $i (1..3) { push @closures, sub { say $i }; } $_->() for @closures; # 3, 3, 3 - oops!
All three closures captured the SAME $i, and by the time we call them, $i is 3.

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:

# 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}->();
The closure version is more concise and arguably more encapsulated - there's no way to access $count except through the provided interface.

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:

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