Mailing List Archive

Deëxperimenting `builtin` functions - should import on `use VERSION`?
In Perl 5.36 we added the `builtin::` package containing several new
functions. Because they were new, they all raised experimental
warnings at compiletime.

These have remained unchanged through 5.38.

Many of these functions seem quite stable now and it would seem to make
sense to remove the experimental warnings from them. I have a PR to do
just that:

https://github.com/Perl/perl5/pull/21330

That all feels fairly uncontentious, but the end-goal of `builtin` had
something more in mind. To quote the original document:

• Once a stable set of functions is defined, consider creating
version-numbered bundles in a similar theme to those provided by
feature.pm:

use builtin ':5.40'; # imports all those functions defined by
# perl v5.40

• Once version-numbered bundles exist, consider whether the main
`use VERSION` syntax should also enable them; i.e.

use v5.40; # Does this imply use builtin ':5.40'; ?

We could now consider either or both of these things.

I'm a *little* hesitant to just do that though by including every
single non-experimental function in a :5.40 bundle and then having it
imported as part of `use v5.40`, because it feels a bit heavy-handed.
All of a sudden now a `use VERSION` declaration is going to start
pulling in quite a few new functions.

I don't think many folks would have much problem with `use v5.40`
activating the `say` feature and also importing the `true` builtin, as:

use v5.40;
say "True is ", true;

But as we continue to add more functions and they become stable, over
time that set of default-imported builtins at the latest version will
continue to grow.

Do we want this? I currently can't think of any particularly
troublesome cases, but I could imagine at some point someone would
object to some particular function being in that bundle because it
conflicted with something else they wanted to do, and thus they
couldn't do `use VERSION` *at all* because of it.

I.e. the trouble with making `use VERSION` do ever-more expanding and
interesting things is that it increases the risk that someone,
somewhere, will find something they object to, and thus can't use it at
all and therefore miss the benefits of it; having to go the long(er)
way around to get the same thing; perhaps a hypothetical

{ use v5.46; } # to hide the scope of default imports
use strict;
use warnings;
use feature ':5.46';
use builtin qw( true false blessed reftype ... );
# all because I didn't want to import builtin::wibble

Am I just being paranoid here? Perhaps they'd find it good enough to

use v5.46;
no builtin 'wibble';

(except that currently unimport of lexicals doesn't exist yet. we
should add that).

What does anyone else think? I'd like to hear arguments for/against such
a `use VERSION` bundling.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: De?xperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
Hi there,

On Tue, 15 Aug 2023, Paul "LeoNerd" Evans wrote:

> ...
> I'm a *little* hesitant to just do that ...

That's good.

> ...
> use builtin qw( true false blessed reftype ... );
> ...

That's surely the way to do it for the next decade or so.

> Am I just being paranoid here? ...

Not in my book.

--

73,
Ged.
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Tue, Aug 15, 2023 at 11:55?AM Paul "LeoNerd" Evans
<leonerd@leonerd.org.uk> wrote:
>
> use v5.46;
> no builtin 'wibble';
>

I'm not sure this is better than making the above work, but what about
something like:

use v5.40 :no-import qw[true] :no-feature qw[say];
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
Of course yes, less boilerplate.

That's true for most built-ins.

And I also wish to for_list to be added to the 5.40 bundle.

That's the shortest way to iterate hashes.



15 avq 2023, Ç.a. 18:55 tarixind? Paul "LeoNerd" Evans <
leonerd@leonerd.org.uk> yazd?:

> In Perl 5.36 we added the `builtin::` package containing several new
> functions. Because they were new, they all raised experimental
> warnings at compiletime.
>
> These have remained unchanged through 5.38.
>
> Many of these functions seem quite stable now and it would seem to make
> sense to remove the experimental warnings from them. I have a PR to do
> just that:
>
> https://github.com/Perl/perl5/pull/21330
>
> That all feels fairly uncontentious, but the end-goal of `builtin` had
> something more in mind. To quote the original document:
>
> • Once a stable set of functions is defined, consider creating
> version-numbered bundles in a similar theme to those provided by
> feature.pm:
>
> use builtin ':5.40'; # imports all those functions defined by
> # perl v5.40
>
> • Once version-numbered bundles exist, consider whether the main
> `use VERSION` syntax should also enable them; i.e.
>
> use v5.40; # Does this imply use builtin ':5.40'; ?
>
> We could now consider either or both of these things.
>
> I'm a *little* hesitant to just do that though by including every
> single non-experimental function in a :5.40 bundle and then having it
> imported as part of `use v5.40`, because it feels a bit heavy-handed.
> All of a sudden now a `use VERSION` declaration is going to start
> pulling in quite a few new functions.
>
> I don't think many folks would have much problem with `use v5.40`
> activating the `say` feature and also importing the `true` builtin, as:
>
> use v5.40;
> say "True is ", true;
>
> But as we continue to add more functions and they become stable, over
> time that set of default-imported builtins at the latest version will
> continue to grow.
>
> Do we want this? I currently can't think of any particularly
> troublesome cases, but I could imagine at some point someone would
> object to some particular function being in that bundle because it
> conflicted with something else they wanted to do, and thus they
> couldn't do `use VERSION` *at all* because of it.
>
> I.e. the trouble with making `use VERSION` do ever-more expanding and
> interesting things is that it increases the risk that someone,
> somewhere, will find something they object to, and thus can't use it at
> all and therefore miss the benefits of it; having to go the long(er)
> way around to get the same thing; perhaps a hypothetical
>
> { use v5.46; } # to hide the scope of default imports
> use strict;
> use warnings;
> use feature ':5.46';
> use builtin qw( true false blessed reftype ... );
> # all because I didn't want to import builtin::wibble
>
> Am I just being paranoid here? Perhaps they'd find it good enough to
>
> use v5.46;
> no builtin 'wibble';
>
> (except that currently unimport of lexicals doesn't exist yet. we
> should add that).
>
> What does anyone else think? I'd like to hear arguments for/against such
> a `use VERSION` bundling.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
Correction: to make it stable silencing the warning (since it's already
part of all bundles believe).

15 avq 2023, Ç.a. 19:35 tarixind? Elvin Aslanov <rwp.primary@gmail.com>
yazd?:

> Of course yes, less boilerplate.
>
> That's true for most built-ins.
>
> And I also wish to for_list to be added to the 5.40 bundle.
>
> That's the shortest way to iterate hashes.
>
>
>
> 15 avq 2023, Ç.a. 18:55 tarixind? Paul "LeoNerd" Evans <
> leonerd@leonerd.org.uk> yazd?:
>
>> In Perl 5.36 we added the `builtin::` package containing several new
>> functions. Because they were new, they all raised experimental
>> warnings at compiletime.
>>
>> These have remained unchanged through 5.38.
>>
>> Many of these functions seem quite stable now and it would seem to make
>> sense to remove the experimental warnings from them. I have a PR to do
>> just that:
>>
>> https://github.com/Perl/perl5/pull/21330
>>
>> That all feels fairly uncontentious, but the end-goal of `builtin` had
>> something more in mind. To quote the original document:
>>
>> • Once a stable set of functions is defined, consider creating
>> version-numbered bundles in a similar theme to those provided by
>> feature.pm:
>>
>> use builtin ':5.40'; # imports all those functions defined by
>> # perl v5.40
>>
>> • Once version-numbered bundles exist, consider whether the main
>> `use VERSION` syntax should also enable them; i.e.
>>
>> use v5.40; # Does this imply use builtin ':5.40'; ?
>>
>> We could now consider either or both of these things.
>>
>> I'm a *little* hesitant to just do that though by including every
>> single non-experimental function in a :5.40 bundle and then having it
>> imported as part of `use v5.40`, because it feels a bit heavy-handed.
>> All of a sudden now a `use VERSION` declaration is going to start
>> pulling in quite a few new functions.
>>
>> I don't think many folks would have much problem with `use v5.40`
>> activating the `say` feature and also importing the `true` builtin, as:
>>
>> use v5.40;
>> say "True is ", true;
>>
>> But as we continue to add more functions and they become stable, over
>> time that set of default-imported builtins at the latest version will
>> continue to grow.
>>
>> Do we want this? I currently can't think of any particularly
>> troublesome cases, but I could imagine at some point someone would
>> object to some particular function being in that bundle because it
>> conflicted with something else they wanted to do, and thus they
>> couldn't do `use VERSION` *at all* because of it.
>>
>> I.e. the trouble with making `use VERSION` do ever-more expanding and
>> interesting things is that it increases the risk that someone,
>> somewhere, will find something they object to, and thus can't use it at
>> all and therefore miss the benefits of it; having to go the long(er)
>> way around to get the same thing; perhaps a hypothetical
>>
>> { use v5.46; } # to hide the scope of default imports
>> use strict;
>> use warnings;
>> use feature ':5.46';
>> use builtin qw( true false blessed reftype ... );
>> # all because I didn't want to import builtin::wibble
>>
>> Am I just being paranoid here? Perhaps they'd find it good enough to
>>
>> use v5.46;
>> no builtin 'wibble';
>>
>> (except that currently unimport of lexicals doesn't exist yet. we
>> should add that).
>>
>> What does anyone else think? I'd like to hear arguments for/against such
>> a `use VERSION` bundling.
>>
>> --
>> Paul "LeoNerd" Evans
>>
>> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
>> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>>
>
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Tue, 15 Aug 2023 19:35:18 +0200
Elvin Aslanov <rwp.primary@gmail.com> wrote:

> And I also wish to for_list to be added to the 5.40 bundle.
>
> That's the shortest way to iterate hashes.

Oh indeed; it's a great feature. It prints `experimental` warnings in
5.36 and 5.38 simply because it is new; but if no problems arise with
it over the course of 5.39 development I expect we'll remove the
warning in 5.40.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
Paul LeoNerd Evanswrote:
>
> In Perl 5.36 we added the `builtin::` package containing several new
> functions. […]
>
> Many of these functions seem quite stable now and it would seem to make
> sense to remove the experimental warnings from them. I have a PR to do
> just that:
>
> https://github.com/Perl/perl5/pull/21330

Great, thank you!


> [version-numbered bundles]
>
> But as we continue to add more functions and they become stable, over
> time that set of default-imported builtins at the latest version will
> continue to grow. […]
>
> Am I just being paranoid here? Perhaps they'd find it good enough to
>
> use v5.46;
> no builtin 'wibble';

When you described the problem, this was the possible solution that immediately came to mind. Seems intuitive enough.

Then again, the builtin:: namespace is always available anyway, so expressions like builtin::false don't even need an import. They are unambiguous and just work, no matter what you import or not. I'd tend to think that this is good enough, at least for the moment.

Also, the original plan you quoted earlier was to add version-numbered bundles only "once a stable set of functions is defined". As long as there is reason to worry that future additions to builtin may cause trouble, the set of builtin functions is probably not stable enough yet.

* * *

One more thing:

When a builtin is imported that matches the name of another imported or declared function, there needs to be a warning. Otherwise, upgrading a `use VERSION` statement might in future silently cause action at a distance.

Consider the following one-liner, which prints the result of builtin::false, not boolean::false. But the only warning it issues is the one about builtin being experimental. I think there should be a "Subroutine redefined" or similar warning.

perl -E 'use v5.38; use builtin "false"; use boolean; say false;'


--
Arne Johannessen
<https://arne.johannessen.de/>
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On 8/15/23 14:38, Bruno Meneguele wrote:
>
> But wouldn't it deviate from the "sane defaults" principle?
> At first glance it, for those non-started developers, aren't builtin
> function "built into the language"? Why would I/they have to import
> them?


[?]
> With that analogy in mind, aren't new stable features inherent to the
> new language release? Also, isn't `use VERSION` already an
> standard/default to every Perl developer, somewhat like `use warnings`
> and `use strict`? Why would one import builtin functionalities?
> Questions that newcomers will rise: if `use VERSION` doesn't bring new
> builtin functionalities, what does it do?
>
> And that's why I'm in favor of bundling builtin with `use VERSION` and
> create a way to "unimport" the feature, like LeoNerd's idea, allowing
> those with better knowledge of the language make their way, while simple
> users and newcomers can enjoy "sane defaults".

Agreed.  I have enough boilerplate additions to my code that I now
create boilerplate modules to include the boilerplate.  Import::Base and
Import::Into for the win.

But I don’t want to do that anymore.

If a user doesn’t want to have the benefits of 5.4X, then they can
simply not update their use version” statement at the top of their
code.  Or, if possible, update their code to make the best use of their
new Perl.

If the code isn’t using a version statement, then they will soon find
out they need it.  This may appear callous, but if you have code of any
sort which depends upon a third party product, be it the Perl
interpreter or a module on CPAN, if you haven’t frozen your system to
prevent any upgrades (especially from CPAN), you are essentially
counting on the benevolence of others to not introduce changes which
break your code.  A bugfix might break your code, and it probably isn’t
hidden behind a ‘use version’ statement.  A new processor might break
your code because it handles floating point operations differently.

Maybe its because I live partly in the C++ world, but I never expect
full compatibility between releases of anything.

If you need it to work, freeze it.  There’s a group at $work which froze
Perl at 5.8.4 because their mission critical applications won’t be
touched again and they need them to keep working.

On the other hand, new users should automatically get the best
experience.  I would like them to experience the joys of modern Perl,
not the boilerplate stuff of yore.
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On 2023-08-15 18:55, Paul "LeoNerd" Evans wrote:

> [...] Am I just being paranoid here? Perhaps they'd find it good enough to
>
> use v5.46;
> no builtin 'wibble';

I prefer the above, so (lexically) all-inclusive.

But that can just be because I don't see real troubles with it yet.
Can anyone give a real-enough example that shows what problems it can cause?

-- Ruud
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
I don't know, how hard it is, but maybe instead of importing symbols in
many scopes just search for them in builtin:: namespace, when nothing
found in any scopes. I mean maybe builtin:: namespace should be made a
part of the symbol searching mechanism.

--
Ivan Vorontsov <ivrntsv@yandex.ru>
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Thu, 17 Aug 2023 21:26:21 +0300
Ivan Vorontsov <ivrntsv@yandex.ru> wrote:

> I don't know, how hard it is, but maybe instead of importing symbols
> in many scopes just search for them in builtin:: namespace, when
> nothing found in any scopes. I mean maybe builtin:: namespace should
> be made a part of the symbol searching mechanism.

Effectively making them like UNIVERSAL but for functions instead of
methods? It sounds kinda dangerous. I think it's better to be a bit
more direct with the code, rather than implying where to find things.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Tue, 15 Aug 2023 17:55:12 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> Many of these functions seem quite stable now and it would seem to
> make sense to remove the experimental warnings from them. I have a PR
> to do just that:
>
> https://github.com/Perl/perl5/pull/21330

That's now been merged.


> Am I just being paranoid here? Perhaps they'd find it good enough to
>
> use v5.46;
> no builtin 'wibble';
>
> (except that currently unimport of lexicals doesn't exist yet. we
> should add that).

So, I've had a go at implementing a `builtin::unimport` so that `no
builtin ...` works. That's here:

https://github.com/leonerd/perl5/tree/builtin-unimport

At first glance it would appear this is good enough, job's done. But
it's not quite this simple. Nested scopes.

$ ./perl -Mstrict -E 'use builtin "true"; { no builtin "true"; } say "True is ", true'
Bareword "true" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

Oops. The unimport from the inside nested scope has made it
inaccessible for the rest of the outer scope. This is due to the rather
simple way it's implemented.

I can think of a couple of options here, but none of them very easy to
implement.

1. Only hide it for the remainder of this scope; make it visible
again afterwards. This would make the-above example valid, and
print "True is 1".

2. Don't permit unimport, except at the same scope level as the
import happened. This would make the-above example a compile-time
failure.

We can't easily take precedent from Exporter-based modules, because
Exporter doesn't actually implement unimport anyway:

$ ./perl -E 'use Scalar::Util "reftype"; no Scalar::Util "reftype"; say reftype []'
Attempt to call undefined unimport method with arguments ("reftype") via package "Scalar::Util" (Perhaps you forgot to load the package?) at -e line 1.
ARRAY

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: De?xperimentin g `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Thu, Aug 17, 2023 at 11:16:07PM +0100, Paul "LeoNerd" Evans wrote:
> On Tue, 15 Aug 2023 17:55:12 +0100
> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
>
> > Many of these functions seem quite stable now and it would seem to
> > make sense to remove the experimental warnings from them. I have a PR
> > to do just that:
> >
> > https://github.com/Perl/perl5/pull/21330
>
> That's now been merged.
>

+1

> > Am I just being paranoid here? Perhaps they'd find it good enough to
> >
> > use v5.46;
> > no builtin 'wibble';
> >
> > (except that currently unimport of lexicals doesn't exist yet. we
> > should add that).
>
> So, I've had a go at implementing a `builtin::unimport` so that `no
> builtin ...` works. That's here:
>
> https://github.com/leonerd/perl5/tree/builtin-unimport
>

Nice!

> At first glance it would appear this is good enough, job's done. But
> it's not quite this simple. Nested scopes.
>
> $ ./perl -Mstrict -E 'use builtin "true"; { no builtin "true"; } say "True is ", true'
> Bareword "true" not allowed while "strict subs" in use at -e line 1.
> Execution of -e aborted due to compilation errors.
>
> Oops. The unimport from the inside nested scope has made it
> inaccessible for the rest of the outer scope. This is due to the rather
> simple way it's implemented.
>
> I can think of a couple of options here, but none of them very easy to
> implement.
>
> 1. Only hide it for the remainder of this scope; make it visible
> again afterwards. This would make the-above example valid, and
> print "True is 1".
>
> 2. Don't permit unimport, except at the same scope level as the
> import happened. This would make the-above example a compile-time
> failure.
>
> We can't easily take precedent from Exporter-based modules, because
> Exporter doesn't actually implement unimport anyway:

Hmmm.. I would choose 1, which gets closer to what user's are already
used to when working with nested scopes and declarations within it.
But I'm somewhat afraid it might be trickier than 2 to get done :)

--
Bruno Meneguele | HEREDOC.IO
PGP Key: http://bmeneg.com/pubkey.txt
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Fri, 18 Aug 2023 15:25:34 -0300
Bruno Meneguele <bmeneg@heredoc.io> wrote:

> > I can think of a couple of options here, but none of them very easy
> > to implement.
> >
> > 1. Only hide it for the remainder of this scope; make it visible
> > again afterwards. This would make the-above example valid, and
> > print "True is 1".
> >
> > 2. Don't permit unimport, except at the same scope level as the
> > import happened. This would make the-above example a
> > compile-time failure.
> >
> > We can't easily take precedent from Exporter-based modules, because
> > Exporter doesn't actually implement unimport anyway:
>
> Hmmm.. I would choose 1, which gets closer to what user's are already
> used to when working with nested scopes and declarations within it.
> But I'm somewhat afraid it might be trickier than 2 to get done :)

So, a little more on this.

I've spent most of today trying to implement option 1, and so far the
only thing I've determined is that I don't think the savestack will
help.

My initial thought was simply to insert

SAVEI32(pn->xpadn_high);

above the assignment to PL_cop_seqmax, so that when the scope stack is
unwound (which I had hoped would occur at the end of the containing
block), the erasure from the pad would be undone.

But that's not going to fly. The problem is that the syntax

no builtin ...

gets internally interpreted as a BEGIN block containing

BEGIN {
require builtin;
builtin->unimport( ... );
}

and that BEGIN block wraps its own ENTER/LEAVE pair around the
scopestack. Anything that is SAVE*()ed inside the unimport method will
get undone before parsing continues. I think therefore, trying to rely
on the scopestack to implement this won't work.

There may be other tricks I can use here - for example, the block hooks
mechanism. I know that's a thing that exists, but I haven't used it in
detail or really have any idea what it's about yet, so that requires
more research.


I even had a look at option 2, but so far I can't find a way to make
*that* work either. Right now I can't find a way to distinguish the
following two cases:

{ use builtin 'true'; no builtin 'true'; }

{ use builtin 'true'; { no builtin 'true'; } }


If anyone has any ideas on either of those I'd love to hear...

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: De?xperimentin g `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Fri, Aug 18, 2023 at 07:49:40PM +0100, Paul "LeoNerd" Evans wrote:
> I even had a look at option 2, but so far I can't find a way to make
> *that* work either. Right now I can't find a way to distinguish the
> following two cases:
>
> { use builtin 'true'; no builtin 'true'; }
>
> { use builtin 'true'; { no builtin 'true'; } }
>
>
> If anyone has any ideas on either of those I'd love to hear...

I haven't looked closely, but my understanding is that builtin works by
inserting lexical state subs into the caller's context?

So

use builtin 'true';

has the same compile-time effect as if that line said

state sub true { ... };

except that the sub is actually aliased to an existing sub.

And you want fine-grained lexical scoping of that state sub?

Here's how lexical scoping works in the pad.

There is a compile-time variable, PL_cop_seqmax, which is incremented at
each scope-significant point in compilation, such as at the start of a new
block, or after a lexical has been introduced.

Every lexical item in the pad has a COP sequence range,
COP_SEQ_RANGE_LOW()..COP_SEQ_RANGE_HIGH(). The lower value is the value of
PL_cop_seqmax when the lexical item was introduced, and the higher, the
value at the point when it went out of scope. When the parser has not yet
reached end of scope, the upper value is set to PERL_PADSEQ_INTRO IIRC.

This allows the parser to choose the right variant of a lexical in a
single pad while compiling. For example in

{
my $x;
{
my $x;
}
my $x; # warns, but legal
$x # HERE
}

at point HERE in compilation, the first $x may have a range 10..15 say,
the second, 12..13, and the third, 16..PERL_PADSEQ_INTRO.

So when the parser at 'HERE' looks up $x in the pad, it finds three $x's,
but if PL_cop_seqmax currently has the value 18 say, then only the third
$x is still in range (aka in scope).

So in principle what builtin::unimport() should be doing is changing the
COP_SEQ_RANGE_HIGH() setting of the state function from PERL_PADSEQ_INTRO
to the current value of PL_cop_seqmax to signify that it is no longer in
scope.

(I've skipped over the details of 'introducing', which makes
my $x = ...; my $x = $x;
work, because it's probably not relevant.)

The details may be a bit more subtle than that, but its 20 years since I
created the pad.c and pad.h files and changed a lot about how closures etc
work, and I've forgotten most of the details.


--
Hofstadter's Law: It always takes longer than you expect, even when you
take into account Hofstadter's Law.
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Sat, 19 Aug 2023 09:23:01 +0100
Dave Mitchell <davem@iabyn.com> wrote:

> Here's how lexical scoping works in the pad.
...
> So in principle what builtin::unimport() should be doing is changing
> the COP_SEQ_RANGE_HIGH() setting of the state function from
> PERL_PADSEQ_INTRO to the current value of PL_cop_seqmax to signify
> that it is no longer in scope.

Yup - that works but only in simple cases.

In the simple case, doing something like

{
sub true { return "boo!" }
use builtin 'true';

say "Before, true is ", true;
no builtin 'true';
say "After, true is ", true;
}

Behaves as expected:

Before, true is 1
After, true is true


But, simply setting COP_SEQ_RANGE_HIGH() to PL_cop_seqmax has a
long-lasting and not lexically-scoped effect. Meaning, the pad entry is
now always invisible from this point onwards, even in outer scopes. This
means you can't locally hide a builtin within one scope and expect it
to come back:

{
use builtin 'true';
{
no builtin 'true';
}

say "True is ", true;
# ^-- This line fails to compile because there is no true
}

If `use builtin` is allowed to be nested arbitrarily and behave
lexically within that scope, we'd expect the effects of `no builtin` to
behave similarly. A `no builtin` within some small scope should remove
the lexical only within that scope, having it reäppear again in the
outer scope.


So next I thought of SAVEU32()ing the current value of the
COP_SEQ_RANGE_HIGH onto the savestack, in the hope that after the
block is unwound it gets restored back to the "still live, I'm not dead
yet" value, but alas that doesn't work. The `no builtin ...` syntax
implies a BEGIN/END block around it, meaning that the savestack is
already unwound by the very next statement. This has the effect that
`no builtin` can't hide anything any more.

At present I can't think of a way to implement the hiding correctly in
this nested-scope scenario, but neither can I think of a way of
detecting and forbidding it. It'd be great if we could implement it,
but if we can't make it work, then I feel it should at least give a
compiletime failure of "You can't hide lexical in an inner scope" if
anyone attempts it.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: De?xperimentin g `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Mon, Aug 21, 2023 at 05:06:55PM +0100, Paul "LeoNerd" Evans wrote:
> Yup - that works but only in simple cases.

Ok, how about adding a 'negative' flag to xpadn_flags. Where if you find a
match in the pad, but that match has the negative flag, it means 'you
didn't find anything, and don't search further'. So with

{
use builtin 'true';
{
no builtin 'true';
X;
}
Y;
}

there are two pad entries with the name '&builtin', with the expected
ranges (a wide range and a narrower range). When the parser reaches point
X, a lookup of '&true' finds the inner entry, but it's flagged as
negative, so the search stops, failure is returned, and the parser looks
for a package name instead.

At Y, the inner &true is not in range, then the outer &true is found and
is in range, and everyone's happy.

Seems better than messing with some complex save/restore scheme.

--
I thought I was wrong once, but I was mistaken.
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Mon, 21 Aug 2023 20:13:11 +0100
Dave Mitchell <davem@iabyn.com> wrote:

> On Mon, Aug 21, 2023 at 05:06:55PM +0100, Paul "LeoNerd" Evans wrote:
> > Yup - that works but only in simple cases.
>
> Ok, how about adding a 'negative' flag to xpadn_flags. Where if you
> find a match in the pad, but that match has the negative flag, it
> means 'you didn't find anything, and don't search further'.
...
> Seems better than messing with some complex save/restore scheme.

Ahah! Yes; that sounds like a much simpler scheme in fairness.

I'll have a hack at that tomorrow.

Though, in a way, a shame if it does work because then while I'll be
able to add it to 5.39.3, I won't be able to backport the idea of
`no builtin ...` to previous perls. But I guess, not too bad, because
we only want to add `no builtin` to let people remove stuff that
`use VERSION` turned on and right now in those same older perls it
doesn't do any, so... No need for it.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Deëxperimenting `builtin` functions - should import on `use VERSION`? [ In reply to ]
On Mon, 21 Aug 2023 22:13:30 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> On Mon, 21 Aug 2023 20:13:11 +0100
> Dave Mitchell <davem@iabyn.com> wrote:
>
> > On Mon, Aug 21, 2023 at 05:06:55PM +0100, Paul "LeoNerd" Evans
> > wrote:
> > > Yup - that works but only in simple cases.
> >
> > Ok, how about adding a 'negative' flag to xpadn_flags. Where if you
> > find a match in the pad, but that match has the negative flag, it
> > means 'you didn't find anything, and don't search further'.

I ended up calling this a "tombstone".

> > Seems better than messing with some complex save/restore scheme.
>
> Ahah! Yes; that sounds like a much simpler scheme in fairness.
>
> I'll have a hack at that tomorrow.

This is now ready awaiting review:

https://github.com/Perl/perl5/pull/21426

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/