Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Should we Localize $_ ?

by John M. Dlugosz (Monsignor)
on Jun 13, 2001 at 23:10 UTC ( [id://88165]=perlquestion: print w/replies, xml ) Need Help??

John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

In Seven Uses of local, the unnamed writer at TPJ says that
foreach (@array) { ... $f = getfile($filename); ... }
will blow away the values in @array because of the way foreach aliases $_ with the elements, and that function happens to (implicitly) use $_.

So if that's such a problem, why don't we run into it more often? Should we write all our functions to localize $_ just in case someone wants to call it within a foreach loop? Or should the coder of a foreach name his variable or localize it himself if his body is nontrivial?

—John

Replies are listed 'Best First'.
Re: Should we Localize $_ ?
by petdance (Parson) on Jun 13, 2001 at 23:19 UTC
    I think it falls on the programmer's shoulders to make sure her code does not expect that the function isn't going to mangle $_. There are just too many places that $_ could get screwed up.

    Besides, I like to explicitly define what the item is that I'm dealing with. I find it much more sane to say, for example:

    for my $isbn ( @isbns ) { dosomething($isbn);
    than just implicitly saying
    for ( @isbns ) { dosomething($_);
    Besides, in the latter case, you have to ask yourself a question: "Where is the $_ coming from, and what is it?" It should be every programmer's job to eliminate any such potential questions from the minds of future maintainers, even if it's herself.

    Can you tell I'm working on my departmental coding standards? :-)

    xoxo,
    Andy

    %_=split/;/,".;;n;u;e;ot;t;her;c; ".   #   Andy Lester
    'Perl ;@; a;a;j;m;er;y;t;p;n;d;s;o;'.  #   http://petdance.com
    "hack";print map delete$_{$_},split//,q<   andy@petdance.com   >
    
      I respectfully but strongly disagree.

      More specifically I think that you either need to ban most uses of map and grep, or you need to make it a policy that it is the responsibility of the function writer to not pollute global variables needlessly, in particular not to pollute $_. It is the responsibility of the user of the code to test specifically for that (if you have testing suites, it should be properly tested there). But it is the job of the writer of a function to be sure that their functions are good citizens.

      If you are not willing to make this be your policy then I strongly recommend that you ban most natural uses of map and grep from your code base for your sanity. Oh, and I would prefer to work elsewhere, thankyouverymuch.

      This gives you no protection. Witness:
      my @foo = (2, 4, 8); foreach my $num (@foo) { $num *= 2; } $, = ",";print @foo;print "\n";

      Like Ovid says below, this is a 'problem' of aliasing. But it actually isn't a problem unless you assign to $_ (or $num, or whatever your iterator is called), which is typically a deliberate decision, and not usually something that happens by accident. Although I can imagine typos (the olde =/== switcheroo), or some weird precedence error doing this assignment. Also, if your &dosomething operates on its @_ directly, you'll be at risk; i.e., this does the same thing as the above:

      my @foo = (2, 4, 8); foreach my $num (@foo) { &double($num); } $, = ",";print @foo;print "\n"; sub double { $_[0] = 2*$_[0]; return $_[0]; }
      A safe version of &double would be:
      sub double { my $num = $_[0]; $num = 2*$num; return $num; }
      I suspect that this is probably the most likely way the aliasing feature can botch your expected results, but you're all clear so long as all your subs never access their own @_ except to assign it to lexical variables.

      -- Frag.

      It also prevents self confusion when you later add an inner loop:
      for (@isbns) { for (@$_->{Authors}) { # now $_ is something else :) } }
      Hi petdance, do you have a copy of your dept coding standards by the way? I haven't seen this kind of doc for Perl programs before, and although our Perl team is only three people, it could be *very* interesting... thanks sk8boardboy
(Ovid - problems with aliasing) Re: Should we Localize $_ ?
by Ovid (Cardinal) on Jun 13, 2001 at 23:32 UTC

    The issue here is not specifically with the magical $_ var, but with aliasing. Perl has a number of cases where variables are aliased, but it's not always clear when this occurs. I consider this to be a feature, and not a bug, but some would disagree. Here are some examples:

    # poorly implementing VBScript's 'trim' function: trim( $somevar ); sub somevar { $_[0] =~ s/^\s*//; $_[0] =~ s/\s*$//; }

    Ignoring, for the moment, that this is poor code (it probably should return the new value rather than altering the argument - don't get me started on chomp and chop :), this works because the values in @_ are aliases to the original values passed to them.

    Another example:

    my @array = qw/ 1 2 3 4 5 /; my @new_array = sort { $a++ <=> $b-- } @array;

    It looks innocent enough, but @array is now set to: qw/ 0 2 3 4 6 /. Oops! That's probably not what we intended.

    You also have the same behavior with $_ in map and grep operators. Even though $_ is involved here, it's a case of needing to be aware of when Perl is aliasing variables, as opposed to copying them

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Should we Localize $_ ?
by Henri Icarus (Beadle) on Jun 13, 2001 at 23:38 UTC
    The The St. Larry Wall Shrine pointed me to Larry's very interesting article Natural Language Principles in Perl which talks about "Local Ambiguity" (search for it). In natural languages you can construct sentences where it's very hard to figure out who the pronouns refer to, and if you (as the listener) get it wrong it may disastrously alter the meaning of the sentence. That's just a side effect have having a powerfull language where it's easy to say things because you can say them economically. Larry said it better:
    You learn a natural language once and use it many times. The lesson for a language designer is that a language should be optimized for expressive power rather than for ease of learning. It's easy to learn to drive a golf cart, but it's hard to express yourself in one.

    Like the previous poster I tend to use an explicit variable for complicated foreach loops because I don't like to use $_ explicitly in function calls or other places far from the source of implicit variable. However, I love it when I can say:

    last if /somregexp/;
    instead of
    last if $somelongvarname =~ /somregexp/;
    not to mention just plain old
    chop;

    -I went outside... and then I came back in!!!!

Re: Should we Localize $_ ?
by bikeNomad (Priest) on Jun 13, 2001 at 23:16 UTC
    One of the reasons we don't see it often is that $_ is usually used in a context close to where it's set. So we see it used inside foreach loops, inside of <> constructs, etc. Usage of $_ is rare (in my experience) outside of an explicit context that sets it. You'd have to be calling a subroutine that modifies $_ without providing a context that sets it to have a problem like this.
      I don't see any difference between "modifying" and "setting" $_. If the sub called a a built-in that returns a result in $_ (like magic while(<>)), it will clobber the value in the caller's foreach.

      If the sub aliased it, using local or something like foreach which I assumes rebinds without changing the value, that would indeed be different.

      I'm supposing that the reason I don't see this as a problem in real scripts is that I don't use the implicit loop with nontrivial bodies—I name the iteration variable. I suppose people who heavily use map might get bit with it more.

      —John

        I don't get what you're saying. If you have a construct like while (<>) or foreach(@arr) you get a newly scoped $_:
        sub doit { my @array = qw(1 2 3 4); # this doesn't damage outer @array: $_ = 'xyz' foreach (@array); } my @array = qw(a b c d); print join("|", @array), "\n"; foreach (@array) { my $f = doit('abcd'); } print join("|", @array), "\n";
Re: Should we Localize $_ ?
by jynx (Priest) on Jun 13, 2001 at 23:28 UTC

    This is an interesting question. While entirely uncertain if i have the best coding policy, usually i break it down this way:

    • Localize $_ on every subroutine/method that uses $_ where it's not implicitly localized for you
    • Localize $_ on every subroutine/method where it's ambiguous whether $_ will be localized for you or not.
    • Use a variable for every foreach that calls other subroutines/methods in its body.
    • Use a variable if there's the possibility that $_ will be used (explicit or implicit) and possibly clobbered in a foreach.
    • Always check to make sure your values are where you left them, don't assume $_ will be localized for you!

    Note how i localize $_ in subroutines whether i know it's going into a loop structure or not. This assures i'm not taken by surprise when i have to add an extension because my manager didn't tell me all the specs to start with :)

    These are just my personal habits, i'd be happy to hear other monks outlook on the matter as well.

    Hope This Helps,
    jynx

Re: Should we Localize $_ ?
by TGI (Parson) on Jun 14, 2001 at 01:09 UTC

    I thought I'd point out that the unnamed writer at TPJ is none other than the perlmonk, Saint Mark Dominus.


    TGI says moo

      That's good to know. So why doesn't TPJ let him use a biline? Perhaps it's a scoping issue—he assumes that everyone knows that articles reprinted on that domain belong to him. No name is mentioned on the page.
Re: Should we Localize $_ ?
by dragonchild (Archbishop) on Jun 13, 2001 at 23:16 UTC
    Wow!! Uhhh... I just tried out the code example from 7 Uses of local and I have to say that I'm rather concerned. It's something I've never even seen happen before, and I love my $_! Ouch ...

      I can remember the first time I came across this. I had always just assumed that $_ was localised, and the book "Programming Perl" didn't even mention that it was a dangerous global.

      So I checked back through most of the complex code I was writing, found it didn't affect any of my other code, told my programming partner in cryme about it, and made a policy of not calling any functions when I was making use of $_. ie, I decided that if your loop is big enough to call functions, you should be naming the loop variable.

      I think there should be really big warnings about this, though.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://88165]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (6)
As of 2024-03-28 20:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found