May 25, 2022·6 min read·engineeringperlgovernancecompatibilityfeatures

Perl keeps compatibility while changing its versioned defaults

The Perl Steering Council has rejected a compatibility-breaking Perl 7 baseline. Lexical feature selection provides a practical way to change new code without reinterpreting old source.

Perl's dispute over changing defaults has produced a concrete compatibility policy. In its May 25 explanation of the Perl 7 plans, the Perl Steering Council says it has rejected the original compatibility-breaking approach. Existing sensibly written Perl 5 code should continue to run, subject to necessary exceptions such as security repairs. New defaults will be available through feature guards and version bundles. A future major-number change remains possible, but would still preserve Perl 5 compatibility by default and require an explicit declaration to select the new behavior.

That decision has consequences beyond the release number. A runtime can support several sets of language rules while each source file declares the rules it needs. The source declaration becomes a compatibility mechanism. It allows new code to opt into stricter behavior without requiring every existing script to change before its interpreter can be upgraded.

I value Perl most for direct text manipulation, including small programs that do not justify much surrounding structure. A policy that keeps those programs runnable matters to me. It also needs a practical route for making newly written Perl easier to inspect, since preserving old source alone does not improve the next program someone writes.

Lexical features give source code a local meaning

The feature manual in the available 5.36 release candidate describes the underlying problem. Adding syntax can change the meaning of existing programs. A feature pragma limits parsing of a new construct to scopes that enable it. This is a specific answer to compatibility, with a boundary defined by the program text.

use strict;
use warnings;

{
    use feature 'say';
    say "available in this block";
}

print "outside the block\n";

The feature is available from its declaration to the end of the enclosing block. It does not become a package-wide setting merely because one part of a package enabled it. Lexical scope gives readers a way to determine which constructs apply without finding and executing a global initialization routine first.

Features can also be disabled locally with no feature. That matters when a language facility is too permissive for newly written code. Keeping the facility available in the interpreter does not require every scope to accept it. A stricter scope can reject the syntax while another scope continues to use its established behavior.

This approach makes compatibility more specific than an assertion that an entire runtime is old or new. The interpreter version determines which facilities it can implement. A lexical declaration determines which selectable facilities the current source region uses. Those are separate decisions, and the mechanism exposes both to the person reading the program.

Bundles make a set of choices reproducible

Selecting individual features can become repetitive. The feature manual therefore defines named bundles containing a set of enabled features. A bundle name begins with a colon so Perl can distinguish it from an individual feature name. Requiring a language version through use VERSION implicitly selects the corresponding bundle.

use v5.36;

sub add ($left, $right) {
    return $left + $right;
}

say add(4, 5);

This illustrates the forthcoming release's source declaration and stable signature syntax. It is a declaration for that language version, not a claim that the final release is available today. The third release candidate's tag is dated May 22, so its feature documentation supplies a concrete implementation to examine before the final release.

The documented :5.36 bundle includes signatures and the isa operator alongside facilities such as say and state. It excludes indirect and multidimensional, which are present in the default group. The bundle consequently selects both additions and restrictions. Its meaning cannot be reduced to enabling every construct known to the current interpreter.

Patch versions do not change a bundle's membership. The manual gives :5.14.0 and :5.14.1 as equivalent to :5.14. This makes a source declaration a stable choice of language facilities within the release family. A maintenance update can repair the interpreter without silently selecting a different bundle for the same declaration.

The manual also distinguishes loading a bundle through use feature from declaring a minimum language version. Version declarations at the documented threshold also enable strictness. That difference matters for code generators or style tools which might otherwise treat the declarations as interchangeable text. Choosing a feature set and declaring a program's minimum interpreter version overlap, but their effects need to be read precisely.

Rejecting old syntax can be an explicit choice

The indirect feature permits indirect object method syntax, such as new Foo 1, 2. Turning it off disallows that form. The release-candidate manual notes that it became a named feature in 5.32, after previously being always enabled. Giving an existing behavior a feature name creates the means to opt out of it.

The multidimensional feature demonstrates a more substantial implicit transformation. With it enabled, a hash access written with a list of subscripts becomes a joined string key. The documented transformation is equivalent to joining the expressions with $;. This is emulation of multidimensional indexing through a hash, not a separate multidimensional container.

use strict;
use warnings;
no feature 'multidimensional';

my %cells;
my ($row, $column) = (1, 2);
$cells{join ":", $row, $column} = "occupied";

The example constructs its string key explicitly. In a scope with the feature disabled, the implicit multiple-subscript form would instead produce a compilation error. Applications can choose a different representation, but the important language behavior is that the accidental implicit transformation becomes rejectable.

My preference is for declarations that expose these choices near the code they govern. A stricter feature bundle gives newly maintained source a useful default without requiring the interpreter to forget the older syntax. The compatibility cost moves into maintaining and documenting the selectable behaviors. That is real complexity for the implementation, even when the declaration that selects them is short.

Experimental status is a separate decision

A feature guard does not establish that a construct has a stable interface. The same manual marks switch as experimental and says its implementation may change or disappear. It also documents a separate warning category for that experiment. Enabling syntax and accepting its compatibility status are distinct acts, even if both appear as short pragma statements.

This separation explains why selecting a newer bundle should not mean requesting every available experiment. The bundle table contains switch in earlier version groups but omits it from :5.36. A reader therefore needs the documented membership of the particular bundle, not an assumption that every newer group contains all the earlier group's entries. Removing an experiment from a new default set can coexist with retaining the explicit mechanism that enables it.

For library authors, that distinction affects the meaning of a minimum-version declaration. It selects the rules used by the library's own source. It does not by itself express whether the author accepts an experimental interface elsewhere in the implementation. An explicit opt-in remains useful evidence when reviewing such code.

Warnings add another observable part of the contract. Suppressing an experimental warning changes diagnostic output, but it cannot convert the documented experiment into a stable feature. Keeping that distinction clear avoids treating a quiet compilation as evidence that future compatibility has been promised. The pragma system gives authors tools to make choices, while the feature documentation explains the obligations attached to them.

Governance defines who can maintain that policy

The governance rules shipped with the release candidate assign authority over the language, interpreter, tests, and documentation. They define a Core Team which elects the three-person Steering Council and delegates substantial decision-making power to it. The council can manage releases and establish the process for proposing and deciding language changes.

Those rules prefer consensus and generally applicable processes to repeated rulings on individual cases. They also define limits. The council's authority concerns developing and releasing the language, not unrelated parts of the Perl ecosystem. CPAN authors and community-operated services do not become subordinate to the council merely because they use Perl.

That distinction is relevant to compatibility because an interpreter policy does not automatically update a module author's API contract. A language release can keep parsing old source while a particular dependency has its own requirements. Maintaining an application still involves both contracts. The governance document identifies who owns the interpreter side of that work without claiming ownership of every distribution used with it.

The rules also provide for replacing council members through elections and for modifying governance through defined procedures. Release policy therefore has an institution responsible for making decisions and a process for changing who makes them. That helps distinguish a published compatibility commitment from an individual developer's preference expressed in a discussion.

For someone deciding how to write the next Perl program, the immediate mechanism is already specific. A version declaration selects documented rules, individual pragmas can refine them within lexical scopes, and the interpreter retains the ability to run code using earlier choices. This gives compatibility a representation in the source that can remain visible when the runtime advances.