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.

2 Comments »

  1. 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.

  2. 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.

RSS feed for comments on this post · TrackBack URI

Leave a Comment