November 20, 2017·4 min read·engineeringperlstatesyntax

Perl 5.27.6 initializes persistent arrays and hashes

A state declaration can now initialize a single persistent array or hash. The distinction between one aggregate and a list of variables remains part of the syntax contract.

A persistent array is one variable even when its initial value contains several elements. Perl 5.27.6 recognizes that distinction by allowing an initializer on a single aggregate state declaration. Its release notes give state @a = qw(x y z) as the example. Initializing a list of persistent lexical variables remains unsupported. The release history records November 20, 2017.

That boundary matters because list context and a list of declaration targets are different concepts. An array initializer naturally supplies several values. It does not follow that the declaration names several independent variables. The release updates its diagnostic to remove the misleading suggestion that list-context initialization itself is forbidden.

I find the change useful for small Perl programs that need private state across repeated calls. Keeping an initializer beside its declaration makes the intended lifetime easier to see. The variable still has persistent state, so the compact syntax does not make successive calls independent of one another.

Persistence follows the declaration

The subroutine documentation for this release describes state as a lexical variable with the scoping rules of my and a value that persists between subroutine calls. The feature must be enabled, for example with use feature 'state'. Its documentation dates the keyword to Perl 5.10.0, so persistence itself is not new in this snapshot.

The initialization assignment runs the first time the declaration executes. Later evaluations ignore that initialization. Extending the form to an aggregate therefore lets an array or hash acquire its initial contents once while retaining subsequent modifications.

A complete version of the array example is:

use feature 'state';

sub next_label {
    state @labels = qw(x y z);
    return shift @labels;
}

The declaration describes one persistent array. Re-entering the subroutine does not refill it with the original labels. That behavior is the reason to choose state here, and it is also the reason a reader should not mistake the declaration for an ordinary per-call my initialization.

The release permits the corresponding single-hash form as well. It does not permit initializing a parenthesized list of persistent variables. Writing down that remaining restriction is more accurate than saying the change enables all assignments involving state declarations.

Anonymous subroutines have their own instances

The documentation also explains what happens when a state variable lives inside an anonymous subroutine. Each copy of the subroutine has its own state variable. Calls through the same copy share its persistent value, while a newly constructed copy has a separate instance.

That distinction prevents equating persistence with a single global variable. Lexical visibility and storage lifetime answer different questions. A variable can remain private to the function's source scope while its value outlives a particular call. Creating several anonymous functions can produce several independent persistent values under the documented rules.

Closures offer another way to retain private variables. The documentation shows a lexical variable outside a subroutine but inside an enclosing block. A reference from the subroutine keeps the value alive. state places the lifetime decision at the declaration inside the subroutine, which can be clearer when that is the intended ownership.

I prefer making that ownership visible instead of introducing package variables for convenience. Persistent state is still a dependency between calls, and tests need to account for it. The new syntax helps express where that dependency belongs without extending the variable's lexical visibility.

Exact identifiers need not fit an integer

The snapshot also changes how stat preserves large inode numbers. On platforms where the inode type exceeds Perl's native integer types, it returns large inode numbers as strings of decimal digits. Exact comparison can then use eq. Numeric comparison with == can fall back to floating point and lose the distinction between sufficiently large values.

This is a useful separation between identifiers and quantities. An inode number needs exact identity for equality checks, but arithmetic on it is usually meaningless. Returning its decimal representation preserves information that a rounded floating-point value would discard.

The release does not claim that every platform now returns every inode number as a string. The documented condition concerns values too large for the native integer types on the relevant platform. Code that needs exact equality should follow the documented string comparison instead of assuming that numeric coercion is harmless.

Concatenation combines work inside the compiler

Another implementation change introduces the multiconcat opcode. The compiler combines chains of concatenations and constants, optionally including an assignment or append, into one operation. A new optimization phase scans the complete operation tree before the peephole optimizer.

The source explains that the combined operation stores a constant string and segment lengths alongside the argument count. That lets a mixed constant-and-variable expression retain information about its fixed pieces without executing every concatenation separately. The notes also describe eligible constant-format sprintf expressions containing only %s and %%.

These improvements affect work below the source expression. They do not require replacing a readable string expression with a hand-built series of buffer operations. For a short text program, the persistent aggregate declaration changes what can be expressed directly, while concatenation optimization changes how the existing expression executes.