Front page | perl.perl5.porters |
Postings from March 2023
Re: strictness and "use 5.whatever"
From: Paul "LeoNerd" Evans
March 10, 2023 18:49
Re: strictness and "use 5.whatever"
Message ID: firstname.lastname@example.org
On Fri, 10 Mar 2023 15:16:52 +0100
demerphq <email@example.com> wrote:
> On Fri, 10 Mar 2023 at 12:58, Zefram via perl5-porters
> <firstname.lastname@example.org> wrote:
> > Paul "LeoNerd" Evans wrote:
> > >I'd be happy to review a PR.
> > All aboard the patch train, choo choo!
> Pushed as https://github.com/Perl/perl5/pull/20923
In summary: I don't think I'd like to accept those changes as they
stand, but I'd be happy to copy out a few of the central lines of
change from it, and adjust some version number boundaries in some code.
For more detail and reasoning, see below:
My review comment, copied from github:
Wow, overall a much bigger changeset than I had imagined.
I can see (and overall like) the central change around op.c's lines
7902-7906; that seems to be prettymuch what I had in mind to do, yes.
I'd be happy to extract that little part out and apply it. Though I'd
consider making the cut-off version 5.35, rather than 5.37, so that
perl release 5.36.0 continues to have its current behaviour.
All of the rest though, I'm really unsure why it's there. In
particular I don't see why any of the changes in strict.pm are needed
Additionally, I see that PL_prevailing_version has been removed,
because it was only being used to support the version downgrade
ratchet warning. We'd still verymuch like to keep that warning in
place - it has overlaps with some future intended changes and other
things that I'll explain in more detail in a followup email. The
version number it applies to could perhaps be expanded out (in a
separate change), but we'd like the overall mechanism to remain.
## Version Ratcheting
Under both the 5.11-5.35 and the 5.35-onwards sets of semantics, a
`use VERSION` statement can be understood to be a shorthand for:
"reset a bunch of stuff"
use feature ':SOME_BUNDLE';
where "reset a bunch of stuff" means turning back off all the
strict/warnings/feature bundles, so that the `use` statements only turn
those things on. These resets all work because strict flags, warning
flags, and feature flags are all implemented as dedicated bits in some
special unique lexically-scoped set of storage in the interpreter, and
can be arbitrarily turned on or off, or reset to a known blank state,
at any point.
For example, v5.36 added the `isa` feature, so even if that is turned
on, a subsequent `use VERSION` of an earlier version will disable it
use feature 'isa';
say "BOO" if  isa "ARRAY";
Bareword found where operator expected at - line 4, near "] isa"
(Missing operator before isa?)
String found where operator expected at - line 4, near "isa "ARRAY""
(Do you need to predeclare isa?)
syntax error at - line 4, near "] isa "
Execution of - aborted due to compilation errors.
As part of our umbrella of "making use VERSION simpler to explain", we
thought that if we forbid downgrading from a post-5.11 to a pre-5.11
version (the time at which strict happens), then it becomes a bit
easier to explain without needing to explain this reset. But it's still
complicated because of version bundles.
But we still want to keep the concept of a downgrade ratchet because of
an upcoming change.
Looking ahead to 5.39.1, that will be the first time that we can
declare parts of the `builtin` module as non-experimental. A lot of the
functions that are just a simple clone of the thing from Scalar::Util,
for example. At that point it makes sense for builtin.pm, like
feature.pm, to have version bundles. At which point, it would feel
quite natural for `use VERSION` to activate those things too.
# equivalent to
# use strict;
# use warnings;
# use feature ':5.40';
# use builtin ':5.40';
# now straight away we have true, false, refaddr, reftype, et.al.
At this point it becomes somewhat harder to explain the "reset" part of
a `use VERSION` declaration. builtins are just lexical imports from the
builtin:: namespace, they're not a toggled set of bits in some special
interpreter storage. They're just more functions in the lexical pad. If
a `use v5.40` statement has turned them on, it's rather much harder to
explain or implement what a `use v5.36` statement afterwards would do
say "True is ", true;
# What is `true` here?
At this point, it really does feel like we want a version downgrading
ratchet, to stop people going from a version that applied a bunch of
builtins to the pad, to one that doesn't. To put it another way, we want
to prevent islands of lower versions from being allowed inside a sea of
a later version.
Of course, there's nothing wrong with having them the other way around.
It's perfectly fine to bump upwards to a later version inside an island
within a sea of a lesser version.
say "True is ", true;
# normal lexical scoping rules apply.
Now, all of the-above reasoning only justifies a ratchet that applies
at the version boundary when implicit lexical import of builtins
becomes a thing - that would be at least 5.39.1 and doesn't exist yet.
I remember that there was some reasoning why we also wanted that to
apply right the way back to 5.11, but it could be that the only reason
for that was related to the implict-vs-explicit "strict" bits change
that your other part of the PR undoes anyway. So perhaps we don't need
a ratchet point at the 5.11 boundary any more.
## The `PL_prevailing_version` interpreter variable
All that said, I would verymuch still like to keep the
`PL_prevailing_version` code as part of the interpreter. There are
multiple reasons for having this, beyond just a warning/error ratchet.
The other reason I originally added it is because we don't want to keep
growing more feature bits for all of time. Eventually there *may* come
a point off in the future, when we decide that there's no need to
individually store feature bits for some commonly-enabled features that
honestly everyone just enables all the time anyway. It may be useful at
that point to stop tracking individual feature bits and instead just
say that those features are always enabled if the prevailing version is
greater than some boundary version when it first showed up.
In effect at that point, the set of feature bits represent a
size-bounded sliding window between the oldest "prevailing version"
and the current version, in terms of what features it is possible to
control individually. As well as keeping the implementation slightly
simpler, it also keeps people's mental state simpler when writing or
reviewing code, limits the possible set of interactions between
features when testing, etc etc...
Paul "LeoNerd" Evans
email@example.com | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/