A vision for Perl 7 and beyond

tl;dr: A major version bump should be a semantic versioning change to the default feature bundle.

I’ve heard calls for a Perl 7 along the lines of 5.32 but with better defaults. This document describes how I imagine that might possibly work.

I have not paid any attention to actual discussions/flamewars going on about Perl 7. I have no idea about the technical feasibility of what I describe. And I no longer have time for, nor an economic stake in, contributing to make it happen.

I offer this in the spirit of a knowledgeable community member imagining the future I’d like to live in.

Perl 5 already has a mechanism for opting into different semantics via the feature pragma. Features are granular bits of syntax or semantics like say or unicode_strings. These are organized into ‘feature bundles’, like :5.12, which are enabled via the feature pragma use feature ':5.12', or via a shortcut like use v5.12.

[For the moment, I’m going to ignore the peculiarity that use feature ':5.12' doesn’t set strict but use v5.12 does.1]

This has the very nice property that any Perl code that declares a requirement on a version of Perl gets the semantics released with that version, even when run on a newer Perl.

Because the feature bundles effectively follow semantic versioning, each minor version might add features to the default bundle for that version. For example:

:5.14     say state switch unicode_strings

:5.16     say state switch unicode_strings
          unicode_eval evalbytes current_sub fc

If our code was written with use v5.14 – which works on any perl from v5.14 or later – and we decided that we really needed to add fc, we can bump our declaration to use v5.16. This is safe, because we know that minor versions only add features and never remove them.

But let’s say that we decide that the switch feature is a problem, and we want to remove it from the default bundle in the next version of Perl, whatever that might be. Following the rules of semantic versioning, we never remove functionality in a minor version, so we need a major version bump. In this case, 6 is out, so we need 7.

To illustrate this, let’s ignore all the versions of Perl after v5.16 and their features, and imagine a counterfactual history where v7 followed v5.16 with a feature bundle that removes switch and also adds a new, fictional klingon feature:

:7.0      say state unicode_strings
          unicode_eval evalbytes current_sub fc klingon

Seeing a new major version, we’d be careful bumping from use v5.16 to use v7.0 to get klingon because we know there might be a breaking change. If we want to get ‘switch’ back, we can add it back with the feature pragma because it’s still there, just not part of the default bundle:

use v7.0;
use feature 'switch';

This seems like a completely natural reason for major version bumps and a very familiar, explainable mechanism for it.

A major version bump is just a semantic versioning change to the default feature bundle. Code with older declarations gets the older semantics. Code that needs a mix of old and new semantics can restore them with feature.

But how do we remove things that aren’t features? The answer is straightforward: retroactively declare them to be features. This has already happened for indirect. If you look at the feature docs in Perl 5.32, you’ll see that indirect has been added to the :default feature bundle and every bundle after it. This creates the opportunity to remove indirect in a :7.0 bundle.

This works in a wonderfully subtle way. Consider indirect. On Perl 5.30, having no declaration or a use v5.30 declaration gave you indirect because it was implicit. On Perl 5.32, the same is true. If you try to turn off indirect with the feature pragma you need perl 5.32 anyway, so the only sensible use of the feature is this:

use v5.32;
no feature 'indirect';

I think retroactive feature definition is how all removals should work. And if for some technical reason, some piece of Perl’s syntax or semantics that people want to remove can’t be expressed as a feature, then it shouldn’t be removed.

The essential question that I think is at the crux of the controversy over plans for Perl 7 is this: what features are enabled when no use vX pragma is in force? One view is that it should default to the feature bundle of the current version of Perl – i.e. no minimum version declaration means “latest features”, including default strictures.

I think this is misguided.

For one, this is the critical change that breaks legacy code. If no version declaration is instead taken as equivalent to use v5, then legacy code continues to work.

Also, I believe code benefits from version declarations. Code with a version declaration is protected from being run on older perls. If code is assuming v7 semantics, shouldn’t it fail to run on v5.X? And the version declaration provides forward compatibility. It protects code from future versions of Perl that change semantics but provide backwards compatibility. If code is assuming v7 semantics, shouldn’t it continue to run on v8 without modification?

The ‘cost’ of requiring users to write use v7; is small. It’s not hard to teach or learn. It leads to better, safer code. And it is kinder to existing users.

The next conundrum I have is about strict. Currently, we have the somewhat weird situation where use v5.12 or later enables strict, but use feature ':5.12' does not.1

Should use feature ':7.0' enable strict? It would be more consistent with the rest of the feature system: adding a default_strict feature would make the change visible to people comparing feature bundles across versions. But it’s also a break with the past. On balance, I think it would be a benefit. Default warnings could be handled the same way.

On the other hand, I’ve heard through the grapevine that strict by default is complicated (though I’ve heard it in the context of default strict without a version declaration). If leaving the current system alone is less complex to implement, I think that’s fine, too. Most people will be using use v7, not use feature ':7.0' and will get the benefit regardless.

This leads me to consider the one-liner flags -e and -E. If default_strict is not a Perl 7 feature and if no version declaration is equivalent to use v5, then it all works as you expect already in Perl 5. The -e flag is equivalent to code without a version declaration. The -E flag is equivalent to use feature ':<current>' (which doesn’t turn on strict).

If default warnings work like default strict (on with use v7 and not with the :7.0 feature bundle), then it still works consistently as one expects.

But even if default_strict and default_warnings are features enabled with the :7.0 bundle, the definitions of -e and -E are still straightforward:

-e      use v5
-E      use v7; no strict; no warnings;

Why continue to have -E avoid strict and warnings? I think that’s how most people use one-liners and so minimizes annoyance and breakage. And it minimizes conflict with the -w/W/x flags.

The last thing I want to consider is when old features should be removed. If Perl 7 removes indirect from the default bundle, but keeps it available for backwards compatibility (presumably marked ‘deprecated’), how long should it stay available? If Perl 8 removes it, then code that declares use v5 should error because Perl 8 can’t satisfy the feature bundle.

There is cost to maintaining old code and there is cost to breaking user code. I suspect the right answer is that most features should stay around for backwards compatibility for a long time. But this is no different than the deprecation policy we have today and I see no reason for that to change with Perl 7 and beyond.

So what would I like in the Perl 7 feature bundle?

  • Add default strict and warnings – either as part of the feature bundle or enabled only with use v7.
  • Add signatures as a non-experimental feature. I think this requires adding a retroactive prototypes feature to bundles before v7.
  • Remove indirect.
  • Remove switch (because it requires an experimental feature to work).

I think that’s enough. It’s a step into a future where Perl major versions are defined by the breaking feature bundle changes they make.

In my proposed case, that’s removing three features (indirect, switch, and a newly-defined prototypes), and adding two code-breaking features default_strict and default_warnings (either as literal features or via version declaration).

Breaking changes should be rare. But they should be possible. The approach I’ve described makes it possible and minimizes the impact on legacy code.

I don’t know if what I’ve described is technically feasible, but as a user of Perl 5 and hopefully Perl 7, I hope it is.

  1. Technically, use v5.12 and later turn on only the parts of strict that haven’t been explicitly disabled previously. E.g. no strict 'refs'; use v5.12; turns on strict except for strict refs. This complicates the design of Perl 7 default strict a bit, but I think that’s a small detail that can be worked out reasonably any number of ways without jeopardizing the vision I’ve described. ↩︎

•      •      •

If you enjoyed this or have feedback, please let me know by or