ZetCode

Perl closures

last modified October 9, 2026

A closure is a subroutine that remembers the lexical variables that were in scope when it was created. In Perl, every anonymous subroutine automatically closes over the my variables surrounding its definition, and those variables keep their values between calls even after the enclosing block has finished running.

Closures turn subroutines into small objects with private state. They are used to build counters, sub factories, iterators, generators, callbacks, dispatch tables, and memoizing wrappers. In this tutorial we cover Perl closures with practical examples, from simple lexical capture to the gotchas that surprise beginners.

Anonymous subroutines and code references are the foundation of the topic; see the Perl references and Perl subroutines tutorials if those terms are new to you.

Lexical capture

The simplest closure is an anonymous subroutine that uses a variable declared outside itself. The subroutine keeps a reference to that variable, so the value stays reachable as long as the code reference exists.

capture.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub make_greeter {
    my $greeting = "Hello";
    my $name     = "Perl";

    my $greet = sub {
        say "$greeting, $name!";
    };

    return $greet;
}

my $greeter = make_greeter();
$greeter->();
$greeter->();

When make_greeter returns, $greeting and $name are no longer visible to the rest of the program, yet the returned code reference still reads them on every call. The variable is not copied into the subroutine; the subroutine holds a reference to the same storage, which is why the value survives the lifetime of the enclosing block.

$ perl capture.pl
Hello, Perl!
Hello, Perl!

Why my matters

A closure only remembers variables it can capture. Lexicals declared with my are captured, so each call to the enclosing subroutine gets a fresh private variable. Package variables are not captured at all: they are global, so every subroutine that mentions them shares the same storage.

my_vs_global.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

our $SHARED = 0;

sub bump_global {
    $SHARED++;
    return $SHARED;
}

my $g1 = \&bump_global;
my $g2 = \&bump_global;

say "global: ", $g1->(), " ", $g2->(), " ", $g1->();

sub make_counter {
    my $n = 0;
    return sub {
        $n++;
        return $n;
    };
}

my $c1 = make_counter();
my $c2 = make_counter();

say "counter1: ", $c1->(), " ", $c1->();
say "counter2: ", $c2->();
say "counter1: ", $c1->();

Both $g1 and $g2 point at the same named subroutine, so they walk the same global counter. The two closures are different: each call to make_counter creates a new $n, so $c1 and $c2 count independently. This is the property that makes closures useful for holding private state.

$ perl my_vs_global.pl
global: 1 2 3
counter1: 1 2
counter2: 1
counter1: 3

Sub factories

A factory is a subroutine that returns a specialized subroutine. Because the parameters of the factory are themselves lexicals, they become part of the closure, which lets one factory produce many variants of the same behaviour.

factories.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub make_counter {
    my ($start, $step) = @_;
    my $value = $start;
    return sub {
        my $current = $value;
        $value += $step;
        return $current;
    };
}

sub make_accumulator {
    my $total = 0;
    return sub {
        my ($amount) = @_;
        $total += $amount;
        return $total;
    };
}

my $odds = make_counter(1, 2);
say "odds: ", join(" ", map { $odds->() } 1 .. 4);

my $acc = make_accumulator();
say "acc: ", $acc->(10);
say "acc: ", $acc->(5);
say "acc: ", $acc->(-3);

my $other = make_accumulator();
say "other: ", $other->(100);

make_counter captures both the start value and the step, so the same code produces odd numbers, even numbers, or a countdown depending on the arguments. make_accumulator captures a running total and takes its argument at call time, which shows that a closure can combine remembered state with new parameters. Note that $acc and $other never interfere, because each factory call allocated a separate $total.

$ perl factories.pl
odds: 1 3 5 7
acc: 10
acc: 15
acc: 12
other: 100

Iterators

An iterator is a closure that returns the next element of a sequence on each call and a false value when the sequence is exhausted. The position is kept inside the closure, so the caller needs no index variable.

iterator.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub make_iterator {
    my @items = @_;
    my $index = 0;
    return sub {
        return undef if $index >= @items;
        return $items[$index++];
    };
}

my $iter = make_iterator("alpha", "beta", "gamma");

while (defined(my $item = $iter->())) {
    say "item: $item";
}

my $again = make_iterator(1, 2, 3);
while (defined(my $n = $again->())) {
    say "n: $n";
}

my $empty = make_iterator();
say "empty: ", defined $empty->() ? "has value" : "no more items";

The loop condition must test with defined, because a legitimate element can be the empty string or the number 0, both of which are false in boolean context. Declaring my $item inside the while condition keeps the loop variable lexical. Each iterator owns its own copy of @items and $index, so several iterators can walk the same kind of sequence at the same time.

$ perl iterator.pl
item: alpha
item: beta
item: gamma
n: 1
n: 2
n: 3
empty: no more items

Generators

A generator is an iterator that computes its values instead of storing them. Because the state lives in the closure, an infinite or very large sequence costs nothing until values are actually requested.

generator.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub make_fib {
    my ($a, $b) = (0, 1);
    return sub {
        my $value = $a;
        ($a, $b) = ($b, $a + $b);
        return $value;
    };
}

my $fib = make_fib();
say "fib: ", join(" ", map { $fib->() } 1 .. 10);

sub make_range {
    my ($from, $to) = @_;
    my $next = $from;
    return sub {
        return undef if $next > $to;
        return $next++;
    };
}

my $range = make_range(1, 5);
my $sum = 0;
while (defined(my $n = $range->())) {
    $sum += $n;
}
say "sum 1..5 = $sum";

my $lazy = make_range(1, 1_000_000_000);
say "first three: ", join(" ", $lazy->(), $lazy->(), $lazy->());

make_fib keeps two numbers in the closure and shifts them on every call, so the sequence grows without a stored list. make_range is lazy: make_range(1, 1_000_000_000) returns immediately because nothing is computed until the code reference is called. The same trick powers line readers, tree walkers, and streaming parsers.

$ perl generator.pl
fib: 0 1 1 2 3 5 8 13 21 34
sum 1..5 = 15
first three: 1 2 3

Closures as callbacks

A code reference is an ordinary scalar, so it can be passed to map, grep, and sort. A closure used this way can carry configuration, such as a threshold or a sort order, without extra global variables.

callbacks.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @nums = (1 .. 10);

my $square = sub { $_[0] * $_[0] };
my @squares = map { $square->($_) } @nums;
say "squares: @squares";

my $is_even = sub { $_[0] % 2 == 0 };
my @evens = grep { $is_even->($_) } @nums;
say "evens: @evens";

my ($limit) = (5);
my $below = sub { $_[0] < $limit };
say "below $limit: ", join(" ", grep { $below->($_) } @nums);

my @words = qw(banana Apple cherry date);
my $by_length_then_alpha = sub {
    return length($a) <=> length($b) || $a cmp $b;
};
my @sorted = sort { $by_length_then_alpha->() } @words;
say "sorted: @sorted";

Inside map and grep the current element is available as $_, so the helper subroutines read their argument as $_[0]. The sort closure relies on the special $a and $b package variables, which sort localizes for the duration of the block. The comparison first sorts by length and then falls back to alphabetical order, which is why the uppercase Apple sorts before the lowercase words of length five.

$ perl callbacks.pl
squares: 1 4 9 16 25 36 49 64 81 100
evens: 2 4 6 8 10
below 5: 1 2 3 4
sorted: date Apple banana cherry

Dispatch tables

A hash of code references replaces a long if/elsif chain with a lookup. Each entry is a closure that already captures whatever it needs, and the handler is selected by name at run time.

dispatch.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my %commands = (
    add      => sub { $_[0] + $_[1] },
    subtract => sub { $_[0] - $_[1] },
    multiply => sub { $_[0] * $_[1] },
    max      => sub { $_[0] > $_[1] ? $_[0] : $_[1] },
);

for my $request ([add => 3, 4], [subtract => 10, 4],
                 [multiply => 6, 7], [max => 9, 2]) {
    my ($name, @args) = @$request;
    if (my $handler = $commands{$name}) {
        say "$name(@args) = ", $handler->(@args);
    } else {
        say "unknown command: $name";
    }
}

my $missing = "divide";
say "unknown command: $missing" unless $commands{$missing};

my @known = sort keys %commands;
say "known commands: @known";

Values are taken from the hash and stored in $handler before the call, and the existence test if (my $handler = ...) guards against unknown names. That guard is required for a different reason than in some languages: a hash of closures always evaluates to a true value when the key exists, but calling an undefined entry would raise an error. Keys are sorted before printing so that the output does not depend on hash ordering.

$ perl dispatch.pl
add(3 4) = 7
subtract(10 4) = 6
multiply(6 7) = 42
max(9 2) = 9
unknown command: divide
known commands: add max multiply subtract

Memoization

Memoization stores the result of a call and returns the cached value the next time the same arguments appear. A wrapper closure around the original subroutine keeps the cache private, so the caller sees no extra argument.

memoize.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $calls = 0;

sub naive_fib {
    my ($n) = @_;
    $calls++;
    return $n if $n < 2;
    return naive_fib($n - 1) + naive_fib($n - 2);
}

sub memoize {
    my ($func) = @_;
    my %cache;
    return sub {
        my ($n) = @_;
        return $cache{$n} if exists $cache{$n};
        return $cache{$n} = $func->($n);
    };
}

my $fib = memoize(\&naive_fib);

say "fib(20) = ", $fib->(20);
say "fib(30) = ", $fib->(30);
say "fib(20) again = ", $fib->(20);
say "underlying calls = $calls";

The wrapper captures %cache and the original code reference. The test uses exists rather than a truth check, so a stored 0 would still be returned from the cache instead of recomputed. The final counter shows that the exponential recursion still runs over two million times in total, but every distinct argument is evaluated only once.

$ perl memoize.pl
fib(20) = 6765
fib(30) = 832040
fib(20) again = 6765
underlying calls = 2714428

state variables versus closure state

Since Perl 5.10, a variable declared with state inside a named subroutine is initialized once and then keeps its value between calls. It gives a single named subroutine persistent private state, without wrapping it in a factory.

state.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub next_state {
    state $count = 0;
    return ++$count;
}

sub make_counter {
    my $count = 0;
    return sub { return ++$count };
}

my $c1 = make_counter();
my $c2 = make_counter();

say "state calls: ", next_state(), " ", next_state(), " ", next_state();
say "closure c1: ", $c1->(), " ", $c1->();
say "closure c2: ", $c2->();
say "state again: ", next_state();

state is the right tool when one subroutine needs one private variable and there is only ever a single instance of it. A closure factory is better when you need independent instances, as with $c1 and $c2 above, or when the state must be created at an arbitrary point in the program. In short: use state for singletons, use closures for instances.

$ perl state.pl
state calls: 1 2 3
closure c1: 1 2
closure c2: 1
state again: 4

The classic for loop closure bug

A C-style for loop declares one variable that is reused on every iteration. If each iteration creates a closure over that variable, all the closures see the same storage and therefore the final value.

loop_bug.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @buggy;
for (my $i = 1; $i <= 3; $i++) {
    push @buggy, sub { return $i };
}
say "shared loop variable: ", join(" ", map { $_->() } @buggy);

my @fixed;
for (my $i = 1; $i <= 3; $i++) {
    my $copy = $i;
    push @fixed, sub { return $copy };
}
say "fresh copy per iteration: ", join(" ", map { $_->() } @fixed);

sub make_adder {
    my ($n) = @_;
    return sub { return $_[0] + $n };
}

my @adders = map { make_adder($_) } 1 .. 3;
say "adders over 10: ", join(" ", map { $_->(10) } @adders);

my @foreach_cbs;
for my $n (1 .. 3) {
    push @foreach_cbs, sub { return $n };
}
say "foreach my: ", join(" ", map { $_->() } @foreach_cbs);

Every closure in @buggy shares $i, which has already been incremented to 4 when the closures are finally called. There are two standard fixes: copy the value into a fresh my variable inside the loop, or move the loop body into a factory such as make_adder. The last loop shows that the list form for my $n (1 .. 3) creates a fresh variable per iteration in modern Perl, so it does not show the bug; only the C-style loop does.

$ perl loop_bug.pl
shared loop variable: 4 4 4
fresh copy per iteration: 1 2 3
adders over 10: 11 12 13
foreach my: 1 2 3

Capturing $_ and @_ aliasing

Two built-in variables behave in ways that surprises closure writers: $_ is a package variable, so a closure that reads it does not capture a private copy, and @_ holds aliases to the caller's arguments, so writing to $_[0] modifies the caller's variable.

capture_gotchas.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @nums = (1 .. 5);

my @doubled = map { my $n = $_; sub { return $n * 2 } } @nums;
say "captured copies: ", join(" ", map { $_->() } @doubled);

my @readers = map { sub { return defined($_) ? $_ : "undefined" } } 1 .. 3;
$_ = "outer";
my @seen;
for my $reader (@readers) {
    push @seen, $reader->();
}
say "read global \$_ later: @seen";

sub shout {
    $_[0] = uc $_[0];
    return $_[0];
}

my $word = "quiet";
my $loud = shout($word);
say "argument after call: $word";
say "returned: $loud";

The first group copies $_ into a lexical, so each closure keeps its own number. The second group does not: the closures read the global $_ at call time, and after it is set to "outer" all three return that value. The shout subroutine demonstrates aliasing: assigning to $_[0] rewrites the caller's $word. Unpack @_ into lexicals with my ($x) = @_; whenever the arguments should be private to the subroutine.

$ perl capture_gotchas.pl
captured copies: 2 4 6 8 10
read global $_ later: outer outer outer
argument after call: QUIET
returned: QUIET

Captured variables keep their referents alive

A closure holds a real reference to every lexical it captures, which keeps the captured value and anything it points to alive. When the closure is discarded, the reference count drops to zero and the object is destroyed.

refcount.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @destroyed;

sub new_handle {
    my ($id) = @_;
    return bless { id => $id }, "Handle";
}

sub make_probe {
    my ($id) = @_;
    my $handle = new_handle($id);
    return sub { return "probing " . $handle->{id} };
}

package Handle;

sub DESTROY {
    my ($self) = @_;
    push @destroyed, $self->{id};
    return;
}

package main;

my $probe = make_probe(1);
say $probe->();
say "after factory returns: [@destroyed]";

my $temporary = make_probe(2);
undef $temporary;
say "after undef: [@destroyed]";

{
    my $scoped = make_probe(3);
    say $scoped->();
}
say "after scope exit: [@destroyed]";
say "still probing: ", $probe->();

The handle created for the first probe is still reachable through $probe, so releasing the factory does not free it. Delegating the code reference with undef drops the last reference and destroys the handle immediately; leaving the block does the same for the third probe. This is why a closure over a large data structure can look like a memory leak: the data is only released when the last code reference that captured it disappears. Modules such as Scalar::Util can inspect reference counts and addresses, but printing them is never done in examples because the values differ between runs.

$ perl refcount.pl
probing 1
after factory returns: []
after undef: [2]
probing 3
after scope exit: [2 3]
still probing: probing 1

Closures versus objects

A closure with state and an object with a method solve the same problem in different ways. The closure hides its data completely and is created with a single call; the object exposes its fields, supports inheritance, and can carry many methods.

closure_vs_object.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

sub make_counter {
    my $count = 0;
    return sub { return ++$count };
}

package Counter;

sub new {
    my ($class) = @_;
    return bless { count => 0 }, $class;
}

sub next {
    my ($self) = @_;
    return ++$self->{count};
}

package main;

my $closure = make_counter();
my $object  = Counter->new();

say "closure: ", $closure->(), " ", $closure->();
say "object: ", $object->next(), " ", $object->next();
say "object field is reachable: ", $object->{count};

Both counters behave identically, but the closure offers no public field: the only way to change $count is to call the code reference. The object stores its state in a hash, which any code can read or overwrite, but it can be extended with more methods, serialized, or blessed into a subclass. A common rule of thumb is to use a closure for a single small behaviour and a class as soon as the data needs a name, documentation, or more than one operation.

$ perl closure_vs_object.pl
closure: 1 2
object: 1 2
object field is reachable: 2

In this article we have covered Perl closures: lexical capture, the difference between my variables and globals, sub factories, iterators, generators, callbacks, dispatch tables, memoization, state variables, the classic loop bug, built-in variable gotchas, reference lifetime, and the relationship to objects. Together these techniques let you attach private state to subroutines without adding global variables to your programs.

Best Practices

Source

Perl subroutines · Perl references and nested data structures · Perl FAQ, subroutines and closures · Perl syntax, state variables · The state keyword · Perl objects · Scalar::Util · List::Util

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.