The evil of a global $_
I often write code like this:
$_->store() for @objects;
and quite often it actually works. But suddenly a piece of code doing this in a loop broke with this error message: Can’t call method “store” without a package or object reference. And quite right, sometimes @objects would contain plain integers.
Unfortunately it wouldn’t be quite easy to track down the relevant change, so enter the perl debugger. Declaring a watch on ‘@objects’ isn’t useful as it triggers each time @objects enters or leaves scope. But saving a reference to @objects in $my::objects and the watching $my::objects->[0] worked.
I had reimplemented the store() method using Data::Walk for walking some structure instead of doing it by hand. And Data::Walk sets $_ to the current node. A due to the aliasing implied in the for-modifier each element in my @objects array was garbled.
Two solutions: The store() method can localize $_ by adding local $_; before using Data::Walk – this works in legacy perl interpreters. The routine looping through @objects can make $_ a lexical variable by adding my $_; before the loop – This is a new feature in Perl 5.10.
An even more robust solution would be to have Data::Walk localize $_ itself AND use a lexical $_ in code where $_ is aliased to important data. See RT #47309.
aero said,
June 24, 2009 @ 6:19 pm
if you want to localize $_ variable.
foreach (@objects) { $_->store() }
or
map { $_->store() } @objects;
Modifier statement doesn’t have own lexical scope.
so $_ in modifier statement is not lexicalized.
http://perldoc.perl.org/perlsyn.html#Statement-Modifiers
The behaviour of a my statement modified with a statement modifier conditional or loop construct (e.g. my $x if … ) is undefined.
Peter Makholm said,
June 25, 2009 @ 9:31 am
I wouldn’t add a modifier in the loop implied bu the for statement modifier. I would localize outside the loop, as in
my $_; $_->store for @objects;That works.