tags:

views:

138

answers:

3

UPD: I updated the question with more details.

In this example:

my $x = 1;
for $x (2..3) { }
print $x;   # 1

$x is localized to the loop.

man perlsyn says that

"... If the variable was previously declared with "my", it uses that variable instead of the global one, but it's still localized to the loop."

But the variables are not the same:

use Devel::Peek;
my $x = 1;
Dump $x;
for $x ( 1 ) { Dump $x }

SV = IV(0x8117990) at 0x8100bd4
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 1
SV = IV(0x8117988) at 0x8100bf8
  REFCNT = 2
  FLAGS = (IOK,READONLY,pIOK)
  IV = 1

Is it an error in docs, or am I missing smth ?

+5  A: 

Every rule needs its exception, and this is one. In a for loop, if the loop variable is a lexical (declared with my), Perl will create a new lexical aliased to the current item in the loop. The OP code could be written as follows:

use Data::Alias 'alias';

my $x = 1;
for (2..3) {
    alias my $x = $_;
    # note that $x does not have dynamic scope, and will not be visible in 
    # subs called from within the loop
    # but since $x is a lexical, it can be closed over
}

Edit: previous example was in Perl pseudocode, answer revised for clarity.

Eric Strom
"Perl will create a local version of a lexical" isn't correct. Perl doesn't create a local version of a lexical, there's no such thing and it doesn't make any sense. It creates a local version of a *global*. In the OP's case the lexical and the global are both called $x. You can see it with this one liner: `perl -wle '$x = 42; for $x (1..5) { print $main::x; }'` vs `perl -wle '$x = 42; for my $x (1..5) { print $main::x; }'`
Schwern
And your code DOES work, if you reference `$::x` (the global $x) explicitly. Otherwise $x is referencing the declared lexical $x. `my $x = 1; for (2..3) { local *x = \$_; print $::x; }` If no lexical $x is declared, then $x refers to the global $x. `$x = 1; for (2..3) { local *x = \$_; print $x; }` Moral of the story: don't make a lexical and a global with the same name.
Schwern
+2  A: 

Properly, it should be called aliasing, to avoid confusion with local(). There are various modules on CPAN that let you do aliasing in other circumstances, too.

ysth
A: 

First, realize that there are global (package scoped) variables and there are lexical variables. They can have the same name. From now own I'll refer to the global $x by its fully qualified name $::x which shorthand for "the global $x package main".

For backwards compatibility, for loops use localized globals unless you tell it otherwise, or $x has already been declared lexical (didn't realize that). So for $x (2..3) {} refers to a localized $::x. for my $x (2..3) {} refers to a lexical $x. In both cases they're scoped inside the loop, sort of like:

for (2..3) {
    my $x = $_;
    ...
}

Except $_ is aliased to $x, not copied.

I believe the above explains why you get different scalars when you use Devel::Peek. Since there is no equivalent of local() for a lexical it is probably declaring a new lexical on the lexical pad inside the for loop, just like the code above.

local($x) also changes the underlying SV because it is doing something like:

my $original = $x;
my $new;
*::x = \$new;

...

*::x = \$original;

ie. making a new scalar, plugging it into the $x slot on the symbol table, and then restoring the old scalar when when the scope is done.

But that's really just supposition. You'd have to dig into the code to find out.

The docs saying that a lexical is "localized to the loop" should not be taken to mean the literal local(). The term local is confusing because other languages, and even Perl programmers, use it interchangeably to mean local() and "lexical scope". local() would probably better be termed temp(). So I think the docs are just being a little sloppy with their terminology there.

Schwern
man perlsyn: ... "If the variable was previously declared with "my", it uses that variable instead of the global one, but it's still localized to the loop." This seems to be incorrect..
eugene y
@Schwern perlmod: “If the package name is null, the ‘main’ package is assumed.”
Greg Bacon
@Schwern => your answer is wrong and the global value $::x is never touched. `my $x = 1; for $x (2..3) {print "$x vs $::x\n"}` `$::x` is undef
Eric Strom
@Eric I think something funny is going on, because compare with `perl -wle '$x = 1; for $x (2..3) {print "$x vs $::x\n"}'`
Schwern
@eugene Huh, I never realized it picked a lexical or a global depending on if it was already declared. Thought it always defaulted to the lexical. My bad.
Schwern
@Schwern => compare the following: `perl -wle '$x = 1; for $x (2..3) {print "$x vs $::x\n"}'` ('2 vs 2', '3 vs 3'), `perl -wle '$x = 1; for my $x (2..3) {print "$x vs $::x\n"}'` ('2 vs 1', '3 vs 1'), and `perl -wle 'my $x = 1; for $x (2..3) {print "$x vs $::x\n"}'` ('2 vs undef', '3 vs undef'). What surprised me most about looking into this was that you can properly close around `$x` in the third style loop (outer my declaration). I had thought type 2 declarations were required (new lexical each time) rather than a single outer declaration. (this is unlike how many other languages work)
Eric Strom
... character limit: for example, if you try to do that type of closure in javascript, each closure will close around the same variable, thus all getting the last value it had assigned.
Eric Strom