tags:

views:

193

answers:

2

Why is there no warning thrown for the redeclaration of $i in the following code?

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

for my $i (1..3) {
  my $i = 'DUMMY';
  print Dumper $i;
}
+2  A: 

From man perlsyn:
The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn.

Because of this aliasing, the iteration variable is always localized to the loop, no matter how it is declared. Consider this example:

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

Next, see what flags are set for the iterator variable in your case:

use Devel::Peek;    
for my $i ( 1 ) { # just one iteration
    Dump $i;
}  

SV = IV(0x81178c8) at 0x8100bf8
REFCNT = 2
FLAGS = (IOK,READONLY,pIOK)
IV = 1

Oops. There's no PADMY flag here, which would indicate that its a lexical variable, declared with my.

I hope this explains why there's no warning thrown for the redeclaration of $i in your example..

eugene y
read the perlsyn page before posting, it didn't really clear much up. it was my understanding that both are lexical to the for block. i know the loop var is localized but i would have expected a warning to be generated.
schelcj
You are wrong, the first $i is a lexical variable with a implicit scope that encompasses the for-statement. And no, the loop var is never localized automatically. Localizing a variable in perl submits a preexisting variable to dynamic scoping, a concept quite unique to perl IIRC. See the extended discussion at http://perldoc.perl.org/perlsub.html#Temporary-Values-via-local%28%29
willert
@willert: `local()` is not applicable to lexical variables. The loop variable is ALWAYS localized (but not with local). Again, man perlsyn: "If the variable is preceded with the keyword my, then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. 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 implicit localisation occurs only in a foreach loop."
eugene y
@eugene y: I stand corrected. Interesting what you learn about basic language structures you assumed you know by heart. On the other hand, I never found myself willing to use anything but $_ or a lexical with minimal scope in a for loop and I guess doing so would rightfully be considered worst practice(tm), even in Perl5. On the other hand, the implicit localization in for-loops doesn't mitigate the worst problem: if you do anything to $_ in it's dynamic scope (i.e. in any modular code at all, because anything can be called from a $_ for-loop), you have to localize $_ explicitly beforehand.
willert
Take home message (sometimes 600 chars simply are not enough): whenever you manipulate $_, issue a "local $_;" beforehand. Perl 5.10 has a few changes, that might mitigate this, but I guess it will take years before 5.10 could officially be considered standard, so caveat emptor.
willert
+8  A: 

Actually, you only get warnings for redefinitions in the same scope. Writing:

use warnings;
my $i;
{
  my $i;
  # do something to the inner $i
}
# do something to the outer $i

is perfectly valid.

I am not sure if the perl internals handle it this way, but you can think of your for-loop as being parsed as

{
  my $i;
  for $i ( ... ) { ... }
  # the outer scope-block parens are important!
};
willert
Your second example is not at all the same as the first, because of the different scope, as well as the loop iterator being aliased to the array elements (meaning if you change $i within the loop, you will change your array, or generate a warning if the elements are constants).
Ether
they are both within the same scope of the for block whereas your examples are both in different scopes.
schelcj
The aliasing in the second example works as expected, try it out yourself:my @a = ( 1 .. 3 );{ my $i; for $i ( 1 .. 3 ) { $i = 'x'; } }print "a: @a";As for being in the same scope: as the question shows, the declarationin the for expression is clearly not in the same scope as the declarationin the inner block.
willert
when a `for` loop uses a previously declared lexical as its loop variable, the effect is the same as if the loop variable was declared with `my` in the `for` declaration
Eric Strom
Oh my, I guess, it's been 5 years since I had last looked into perlsyn ... Quote: if variables are declared with my in the initialization section of the for, the lexical scope of those variables is exactly the for loop (the body of the loop and the control sections). I.E. there IS an implicit scope block around the whole control-structure, and my second example is really spot on. Cheers
willert
wouldn't your second example make $i visible outside the for block? from perlsyn "If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop." your example doesn't quite match what i'm ask, or am i missing something?nevermind, didn't read the other responses.
schelcj