develooper Front page | perl.perl5.porters | Postings from March 2013

Re: Salvaging lexical $_ from deprecation

Thread Previous | Thread Next
From:
Nicholas Clark
Date:
March 3, 2013 09:48
Subject:
Re: Salvaging lexical $_ from deprecation
Message ID:
20130303094757.GS3729@plum.flirble.org
On Sun, Mar 03, 2013 at 10:34:20AM +0100, Lukas Mai wrote:

> % perl -wE 'my $_ = "a"; my @fs = map { sub { $_ } } 1, 2, 3; say $_->() 
> for @fs; say'
> Use of my $_ is experimental at -e line 1.
> 1
> 2
> 3
> a
> 
> What's happening here is that map injects a lexical $_ into its argument 
> block, i.e. there's an implicit 'my $_ = <current value>;' after 'map {'.
> 
> Except that's not quite true because the inner $_ is actually an alias 
> to the current value, not a copy. (I don't think you can do this with 
> "normal" lexical variables ('my *_ = \<current value>;' or something?).)
> 
> So each of the 'sub { $_ }' functions ends up closing over a different 
> $_ variable, which is why every element of @fs returns a different value 
> when called. And of course the original $_ is completely unaffected by 
> this despite there being only one 'my $_' declaration in the program, 
> which normally means you're only dealing with one variable with a given 
> name. But here you get four for the price of one.
> 
> This behavior (map implicitly creating more lexical $_'s in its argument 
> block) is triggered by there being a 'my $_' anywhere in map's scope.

I wasn't aware of the above fun. What strikes me as more fun still is that
it's radically different output if you don't have a lexical $_ in scope:

$ ./perl -Ilib -wE '$_ = "a"; my @fs = map { sub { $_ } } 1, 2, 3; say $_->() for @fs; say'
CODE(0x10081bfe8)
CODE(0x10081bfe8)
CODE(0x10081bfe8)
a

$ ./perl -Ilib -wE 'our $_ = "a"; my @fs = map { sub { $_ } } 1, 2, 3; say $_->() for @fs; say'
CODE(0x10081bfb8)
CODE(0x10081bfb8)
CODE(0x10081bfb8)
a


even if I add another lexical to defeat the not-a-closure optimisation,
and ensure that what is returned from the map are 3 unique closures:

$ ./perl -Ilib -wE 'my $b; $_ = "a"; my @fs = map { sub { $b; $_ } } 1, 2, 3; say $_->() for @fs; say'
Useless use of private variable in void context at -e line 1.
CODE(0x1008020b8)
CODE(0x10080fa98)
CODE(0x10080fbb8)
a


Yes, I think I can see why all these behaviours fall out naturally from the
implementation. Sort of. The only first-class thing that (I think) map is
doing which Perl can't is that it's localising the lexical $_.

It's all a rather unfortunate collision of implementation details. I'm not
convinced that it makes a clean comprehensible language though.

Nicholas Clark

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About