Mailing List Archive

`local` on lexicals
Perl currently does not permit this:

$ perl -E 'my $x = 123; { local $x = 456; say $x }'
Can't localize lexical variable $x at -e line 1.

Various reasons for this, mostly around "ugh, but local moves the
variable, not the value". The vast majority of users don't want to use
it like that though - they care more about the value, the effect of
temporarily assigning a new value to the variable. Moving the variable
out of a package, hash or array is basically equivalent to saving the
value anyway, so most people don't notice the difference. This explains
why it doesn't work on plain lexicals.

The upshot is that it's not very convenient. Perhaps back in the 5.6
era that wasn't too bad - there weren't really any situations where
localizing a lexical would make much sense. But in more modern
scenarios there are two big cases that come to mind:

1) aliased lexicals:

$ perl -E 'use experimental qw(declared_refs refaliasing);
my @arr = (qw( a b c ));
my \$a1 = \$arr[1];
local $a1 = "d";'
Can't localize lexical variable $a1 at -e line 1.

2) Object::Pad instance slots:

$ perl -MObject::Pad -E 'class Thing {
has $x;
method m { local $x = 123 }
}'
Can't localize lexical variable $x at -e line 1.

I wonder.. since we have save_item() these days, whether `local on a
lexical` should just use that. If it did then both of these cases would
DWIM.

At this point I'll state my interest: I've been converting more of my
existing Perl code to using Object::Pad, and while almost everything
comes out looking a lot neater, there's one slight akwardness in code
that currently does

sub m
{
my $self = shift;
local $self->{something} = 123;
$self->othermethod;
}

I can't just write this as

method m
{
local $something = 123;
$self->othermethod;
}

due to theabove problem. There isn't in fact a neat way to write this.

I did create Syntax::Keyword::Dynamically to solve this, among other
problems; but it seems a bit heavyweight to drag it in just for this.

https://metacpan.org/pod/Syntax::Keyword::Dynamically

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: `local` on lexicals [ In reply to ]
> On Aug 9, 2021, at 10:19 AM, Paul LeoNerd Evans <leonerd@leonerd.org.uk> wrote:
>
> Perl currently does not permit this:
>
> $ perl -E 'my $x = 123; { local $x = 456; say $x }'
> Can't localize lexical variable $x at -e line 1.

IMO it would be lovely if Perl allowed this, as that’s a likely point of confusion for newcomers. It confused me back-when, at least.

-FG
Re: `local` on lexicals [ In reply to ]
From the keyboard of Paul "LeoNerd" Evans [09.08.21,15:19]:

> Perl currently does not permit this:
>
> $ perl -E 'my $x = 123; { local $x = 456; say $x }'
> Can't localize lexical variable $x at -e line 1.

This opens a tin of worms, the tin being "scoping" and the worms "rules".

People already have a hard time grokking the differences between local,
package global and "global global" scope, the differenres between "my"
and "our" and the aliasing effect of "our" in a multiple package file.

Quick, what would sub foo print here?

my $x = "outer";

if (1) {
my $x = "cond inner";
foo();
}

sub foo {
print "$x\n";
}

And what would it print if that line were in place?

local $x = "cond inner";

> Various reasons for this, mostly around "ugh, but local moves the
> variable, not the value". The vast majority of users don't want to use
> it like that though - they care more about the value, the effect of
> temporarily assigning a new value to the variable. Moving the variable
> out of a package, hash or array is basically equivalent to saving the
> value anyway, so most people don't notice the difference. This explains
> why it doesn't work on plain lexicals.

The main difference between lexical my variables and localized globals
is their behavior in space vs. time in a programmer's view, not so much
the difference between moving away the variable or the value.
Lexicals are bound to a scope, e.g. a block i.e. in space, localized
variables are visible in the entire code path starting from where the
localizing took place, until its conclusion.

Said otherwise, local is for localizing and aliasing a global, whereas
"my" says: "this is mine, it pertains to here" and to pass it around
you have to explicitly do so, as parameter to a subroutine, taking a
ref from it or whatever.

A "local my $foo" blurs that difference and introduces a "timely" scoping
for lexicals. Or wouldn't it? Anyways, aliasing a lexical would need another
metaphor and a different keyword than "local". Perhaps "alias"?

"local" is about localizing a global; aliasing a lexical is a different
thing, and

> The upshot is that it's not very convenient. Perhaps back in the 5.6
> era that wasn't too bad - there weren't really any situations where
> localizing a lexical would make much sense. But in more modern
> scenarios there are two big cases that come to mind:
>
> 1) aliased lexicals:
>
> $ perl -E 'use experimental qw(declared_refs refaliasing);
> my @arr = (qw( a b c ));
> my \$a1 = \$arr[1];
> local $a1 = "d";'
> Can't localize lexical variable $a1 at -e line 1.
>
> 2) Object::Pad instance slots:
>
> $ perl -MObject::Pad -E 'class Thing {
> has $x;
> method m { local $x = 123 }
> }'
> Can't localize lexical variable $x at -e line 1.
>
> I wonder.. since we have save_item() these days, whether `local on a
> lexical` should just use that. If it did then both of these cases would
> DWIM.
>
> At this point I'll state my interest: I've been converting more of my
> existing Perl code to using Object::Pad, and while almost everything
> comes out looking a lot neater, there's one slight akwardness in code
> that currently does
>
> sub m
> {
> my $self = shift;
> local $self->{something} = 123;
> $self->othermethod;
> }
>
> I can't just write this as
>
> method m
> {
> local $something = 123;
> $self->othermethod;
> }
>
> due to theabove problem. There isn't in fact a neat way to write this.
>
> I did create Syntax::Keyword::Dynamically to solve this, among other
> problems; but it seems a bit heavyweight to drag it in just for this.
>
> https://metacpan.org/pod/Syntax::Keyword::Dynamically
>
>

0--gg-

--
_($_=" "x(1<<5)."?\n".q?/)Oo. G?\ /
/\_?/(q /
---------------------------- \__(m.====?.(_("always off the crowd"))."?
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: `local` on lexicals (2) - sorry [ In reply to ]
Ooops. I hit Ctrl-x instead of Ctrl-c. Please ignore.

From the keyboard of shmem [10.08.21,02:03]:

> From the keyboard of Paul "LeoNerd" Evans [09.08.21,15:19]:
>
>> Perl currently does not permit this:
>>
>> $ perl -E 'my $x = 123; { local $x = 456; say $x }'
>> Can't localize lexical variable $x at -e line 1.
>
> This opens a tin of worms, the tin being "scoping" and the worms "rules".
>
> People already have a hard time grokking the differences between local,
> package global and "global global" scope, the differenres between "my"
> and "our" and the aliasing effect of "our" in a multiple package file.
>
> Quick, what would sub foo print here?
>
> my $x = "outer";
>
> if (1) {
> my $x = "cond inner";
> foo();
> }
>
> sub foo {
> print "$x\n";
> }
>
> And what would it print if that line were in place?
>
> local $x = "cond inner";
>
>> Various reasons for this, mostly around "ugh, but local moves the
>> variable, not the value". The vast majority of users don't want to use
>> it like that though - they care more about the value, the effect of
>> temporarily assigning a new value to the variable. Moving the variable
>> out of a package, hash or array is basically equivalent to saving the
>> value anyway, so most people don't notice the difference. This explains
>> why it doesn't work on plain lexicals.
>
> The main difference between lexical my variables and localized globals
> is their behavior in space vs. time in a programmer's view, not so much
> the difference between moving away the variable or the value.
> Lexicals are bound to a scope, e.g. a block i.e. in space, localized
> variables are visible in the entire code path starting from where the
> localizing took place, until its conclusion.
>
> Said otherwise, local is for localizing and aliasing a global, whereas
> "my" says: "this is mine, it pertains to here" and to pass it around
> you have to explicitly do so, as parameter to a subroutine, taking a
> ref from it or whatever.
>
> A "local my $foo" blurs that difference and introduces a "timely" scoping
> for lexicals. Or wouldn't it? Anyways, aliasing a lexical would need another
> metaphor and a different keyword than "local". Perhaps "alias"?
>
> "local" is about localizing a global; aliasing a lexical is a different
> thing, and
>
>> The upshot is that it's not very convenient. Perhaps back in the 5.6
>> era that wasn't too bad - there weren't really any situations where
>> localizing a lexical would make much sense. But in more modern
>> scenarios there are two big cases that come to mind:
>>
>> 1) aliased lexicals:
>>
>> $ perl -E 'use experimental qw(declared_refs refaliasing);
>> my @arr = (qw( a b c ));
>> my \$a1 = \$arr[1];
>> local $a1 = "d";'
>> Can't localize lexical variable $a1 at -e line 1.
>>
>> 2) Object::Pad instance slots:
>>
>> $ perl -MObject::Pad -E 'class Thing {
>> has $x;
>> method m { local $x = 123 }
>> }'
>> Can't localize lexical variable $x at -e line 1.
>>
>> I wonder.. since we have save_item() these days, whether `local on a
>> lexical` should just use that. If it did then both of these cases would
>> DWIM.
>>
>> At this point I'll state my interest: I've been converting more of my
>> existing Perl code to using Object::Pad, and while almost everything
>> comes out looking a lot neater, there's one slight akwardness in code
>> that currently does
>>
>> sub m
>> {
>> my $self = shift;
>> local $self->{something} = 123;
>> $self->othermethod;
>> }
>>
>> I can't just write this as
>>
>> method m
>> {
>> local $something = 123;
>> $self->othermethod;
>> }
>>
>> due to theabove problem. There isn't in fact a neat way to write this.
>>
>> I did create Syntax::Keyword::Dynamically to solve this, among other
>> problems; but it seems a bit heavyweight to drag it in just for this.
>>
>> https://metacpan.org/pod/Syntax::Keyword::Dynamically
>>
>>
>
> 0--gg-
>
>

0--gg-

--
_($_=" "x(1<<5)."?\n".q?/)Oo. G?\ /
/\_?/(q /
---------------------------- \__(m.====?.(_("always off the crowd"))."?
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: `local` on lexicals [ In reply to ]
From the keyboard of Matthew Horsfall (alh) [10.08.21,08:54]:

> On Mon, Aug 9, 2021 at 8:03 PM shmem gm@qwurx.de wrote:
>
> A “local my $foo” blurs that difference and introduces a “timely” scoping
> for lexicals. Or wouldn’t it? Anyways, aliasing a lexical would need another
> metaphor and a different keyword than “local”. Perhaps “alias”?
>
> I don’t understand your concerns here. This is already something you
> can basically do:

My concerns (and entirely mine) are just this: I have a mental model
about what perl is and how it works ("that might be entirely wrong,
you tomfool!" I says to me), and localizing a lexical wasn't - for me -
in sync with that, violated an idea or metaphor or whatever, which isn't
worth telling here. Well, since you could ask: in my head, "local" means
giving another value¹ to a global in the current codepath, without at all
touching the global for other parts of the program, and without having to
restore the original at paths end; while lexicals pertain only to their
scope. So the change proposed seemed inconsistent to me.

So I was about to raise hand. Composed that mail halfway through, let it
sit and went to #p5p on irc.perl.org, where LeoNerd, Grinnz and xenu did
very kindly and with great patience discuss the issue with me, until I
got the point you are making (thanks LeoNerd):

> use strict;
> use warnings;
>
> my $x = { x => "outer" };
>
> if (1) {
> local $x->{x} = "cond inner";
> foo();
> }
>
> sub foo {
> print "$x->{x}\n";
> }
>
> foo();
> __END__
> cond inner
> outer
>
> Why would localizing a scalar $x = 1 be any more confusing than
> localizing a slot in an array or hash?

It is not. Relevant bits of that chat:

< LeoNerd > Compare also with my @x = ("outer"); ... { local $x[0] =
"cond inner" ... etc
< shmem > LeoNerd: ah, I see. In a my @array, slots can be localized
despite the container being a lexical
< shmem > which doesn't work for plain SVs
< LeoNerd > Quite
< LeoNerd > Exactly
< shmem > that's inconsistent! says the referee and shows his teeth
< shmem > got it now, thanks LeoNerd, Grinnz and xenu
< LeoNerd > :)

So, after re-wiring myself, I went back to the open mail sitting there
in vi (courtesy of alpine), and after cancelling the editor I was about
to cancel the entire, now meaningless, mail. Alas, I hit Ctrl-x instead
of Ctrl-c (the keys are next to each other) and the message was sent.
Sorry for that.

Insight obtained (personal note): I am getting old, and the past has now
more weight than an imagined, thriving future - but I am still able to
learn and widen my ideas. Best with folks like you all.

That said, +1 for the proposal. It is indeed as consistent and perlish as
"our", and I wonder why it wasn't there in the first place.
Ah, metaphors.

Again, sorry for the glitch.
0--gg-

¹) in fact it moves away the variable, I have been told. I haven't thought
through the difference between both, for now it looks to me that either
restoring the variable or the value have the same effect in programming.
It might be different for intrinsics (implementation).

--
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: `local` on lexicals [ In reply to ]
On Tue, Aug 10, 2021 at 10:06 AM shmem <gm@qwurx.de> wrote:

> So, after re-wiring myself, I went back to the open mail sitting there
> in vi (courtesy of alpine), and after cancelling the editor I was about
> to cancel the entire, now meaningless, mail. Alas, I hit Ctrl-x instead
> of Ctrl-c (the keys are next to each other) and the message was sent.
> Sorry for that.

Ah, I wasn't quite following what your other reply meant about Ctrl-c
but now I do :)

Cheers,

-- Matthew Horsfall (alh)