Scoping

state#

Declare a lexically scoped variable whose value persists across calls to its enclosing subroutine.

A state variable has the same visibility rules as a my variable — it is visible only inside the block that declares it — but it is initialised exactly once per enclosing closure, not on every entry to the block. This is Perl’s built-in way to write persistent private variables: counters, memoisation caches, one-time-computed lookup tables, anything you would otherwise hide inside a file-scoped lexical above a sub.

Synopsis#

state $scalar
state $scalar = EXPR
state ($a, $b, undef, $c)
state @array
state %hash
state TYPE $var
state $var : ATTRS

What you get back#

state is a declaration, not a function. Like my it returns the variable(s) it declares, so it can be used in any expression position where the declared variable would appear:

(state $n //= 0)++;                 # declare-and-use in one expression
print ++(state $calls), "\n";       # count calls to this line

The initialiser on the right of = is evaluated only the first time control flows through the declaration in that particular closure instance. On every subsequent entry, the declaration re-exposes the already-initialised variable and the initialiser is skipped.

Persistence scope — what “once” means#

“Once per enclosing closure” is the precise rule. A state variable lives as long as the CV (code value) that contains its declaration:

  • In a named sub, the CV is created once at compile time. The state variable is initialised on the first call and retains its value across every later call for the life of the process.

  • In an anonymous sub / closure, each sub { ... } evaluation produces a fresh CV with its own fresh state slot. Two closures made from the same source do not share state.

  • In a nested block inside a sub, the state still belongs to the surrounding CV — it is not re-created on each entry to the inner block.

sub counter { state $n = 0; ++$n }
counter(); counter(); counter();    # returns 1, 2, 3

my $make = sub { sub { state $n = 0; ++$n } };
my $a = $make->();
my $b = $make->();
$a->(); $a->(); $b->();             # $a sees 1, 2; $b sees 1

Feature gate and availability#

state has always been feature-gated. Two ways to enable it:

  • use feature 'state'; — turns on just this feature.

  • use v5.10; (or any later version bundle, including use v5.36 and use v5.42) — implicit via the version feature bundle. Every modern program already has state available through its use vX.Y line.

Without the feature in scope, state is parsed as an ordinary bareword. To reach the built-in unconditionally, write CORE::state.

use v5.36;                          # 'state' feature is on by default
sub hits { state $n = 0; ++$n }

List declarations and initialisers#

state supports the parenthesised list form for declaring several variables at once, with undef as a placeholder:

state ($x, $y, undef, $z);

What it does not yet support is initialising a list of state variables. Upstream explicitly notes this: initialisation of state variables in a parenthesised list is not currently possible, so the list form is useful only for declaration.

Aggregates — state @array and state %hash — have historically had the same restriction on initialisers (Perl emitted an experimental warning, and in several versions a syntax error, for state @a = (1, 2, 3)). Build the aggregate inside the body instead:

sub primes_below_100 {
    state @p;
    unless (@p) {
        @p = _sieve(100);           # fill on first call only
    }
    return @p;
}

The unless (@p) / unless (%h) idiom above is the portable replacement for an aggregate initialiser.

Declaration placement and same-statement semantics#

Like my, our, and local, a state declaration can appear anywhere an expression is allowed (except inside string interpolation). The new binding takes effect for subsequent statements, not for other mentions of the same variable in the same statement:

package main;
use feature 'state';
our $x = 2;
foo($x, state $x = $x + 1, $x);     # foo() receives (2, 3, 2)
foo($x, $main::x);                  # foo() receives (3, 2)

The first $x and the trailing $x in the same call still refer to our $x; only the next statement sees the state $x.

Examples#

A hit counter — the canonical one-liner:

sub hits { state $n = 0; ++$n }
hits(); hits(); hits();             # returns 1, 2, 3

Memoisation of an expensive pure function:

sub fib {
    my ($n) = @_;
    state %cache;
    return $cache{$n} //= $n < 2 ? $n : fib($n-1) + fib($n-2);
}

Lazy one-time setup of a read-only table:

sub months {
    state @m = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
    return @m;
}

Here the initialiser is a simple list, which works because the RHS of state @m = ... is constant-folded to a list literal and current Perl permits this form; the more general case of a computed list still wants the unless (@m) { ... } pattern above for portability.

Per-closure state (fresh slot per sub { ... } evaluation):

sub make_counter {
    return sub { state $n = 0; ++$n };
}
my $a = make_counter();
my $b = make_counter();
$a->(); $a->();                     # 1, 2
$b->();                             # 1 — independent of $a

Declare-and-use in a single expression:

while (my $line = <$fh>) {
    warn "first line was: $line" if (state $first //= $line) eq $line;
}

Edge cases#

  • Aggregate initialisers. state @a = (1, 2, 3) and state %h = (k => 1) have a long history of being restricted or experimental. When in doubt, declare the aggregate and fill it under unless (@a) { ... } on the first call. Scalar initialisers (state $x = EXPR) have always been fine.

  • Same-statement reuse. A fresh state declaration does not apply to other mentions of the same name within the same statement; those still resolve to whatever was in scope before the declaration. See the foo($x, state $x = $x + 1, $x) example above.

  • Shadowing warning. Redeclaring the same name in the same scope — state $n; state $n; — shadows the first. Under use warnings this emits a warning in the shadow category. Almost always a bug.

  • Anonymous-sub multiplicity. Every evaluation of sub { ... } produces a new CV with its own state slot. If you build many closures from the same source, each has its own independent state — that is a feature, but it surprises people expecting file-scoped persistence.

  • Threads and forks. state lives in the CV, which is cloned on fork (copy-on-write in the OS sense — each process continues with its own copy) and on threads->create (deep-cloned). Two processes or threads do not share a state counter.

  • local does not apply. state variables are lexicals, not package variables, so local cannot save and restore them. Wrap the whole sub in its own scope or use a different mechanism if you need dynamic scoping.

  • Access via CORE::state. When writing code that must parse even without the feature enabled, prefix the keyword: CORE::state $n = 0;.

  • Destruction. state variables live as long as the CV. For a named sub in the main program that means “until global destruction.” Do not put a file-handle or a large buffer in a state variable expecting it to be freed at sub exit — it will not be.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • my — ordinary lexical; reinitialised on every block entry, the natural contrast to state

  • our — lexical alias for a package variable; use when callers need to see or local-ise the value

  • local — dynamic scoping for package variables; orthogonal to state and cannot be applied to it

  • substate lives inside a CV; understanding how named vs anonymous subs differ explains per-closure state

  • useuse v5.10 and later enable the state feature as part of the version bundle

  • undef — legal placeholder inside state (...) list declarations