reason for local($_) ?

J

jaialai.technology

I recently saw some Perl code at work in which the original author
starts every subroutine with the line local($_);
Every.single.one. Like this:

sub whatever{
local($_);
print "Whatever man!\n";
}

Of course that isn't a real example but my point is
that even in subroutines that don't use $_ this is done.
I have no idea in what context this would ever make sense to do?
Any thoughts?
I suspect that this is some construct from Perl versions < 5. Is it?
If so, what did it mean back then?
 
R

Randal L. Schwartz

jaialai> sub whatever{
jaialai> local($_);
jaialai> print "Whatever man!\n";
jaialai> }

jaialai> Of course that isn't a real example but my point is
jaialai> that even in subroutines that don't use $_ this is done.
jaialai> I have no idea in what context this would ever make sense to do?
jaialai> Any thoughts?

Voodoo. It was needed in one place, but he does it everywhere now out
of fear, rather than thinking about it.

It's like all those crazy people who put "i" and "s" modifiers on *all*
regex even if they aren't using any feature affected by it. Silly.

print "Just another Perl hacker,";
 
T

Tim McDaniel

I recently saw some Perl code at work in which the original author
starts every subroutine with the line local($_);
Every.single.one. Like this:

sub whatever{
local($_);
print "Whatever man!\n";
}

Of course that isn't a real example but my point is
that even in subroutines that don't use $_ this is done.
I have no idea in what context this would ever make sense to do?

Um, I usually did that. As you know, $_ is a global variable that's
used implicitly by a lot of operations. local()ing it in a sub makes
absolutely sure that anything done in this sub or under it will not
screw up the caller.

I did it in each sub because it was just part of how I started a sub.
It was very quick to type and not likely to cause much overhead.
Otherwise, when editing a sub, I would have had to consider whether
the new code might change $_, and if so go to the top of the sub to
see whether there's a "local $_" already there. And if you only
"local $_" when it's necessary, then there's a chance that you'll
overlook a code path and fail to local() it, causing a bug that may be
subtle.

It's true that 95% of the time I used $_, it was as the implicit
control variable of a foreach loop, which automatically localizes its
control variable. But better an unnecessary local than a silent bug.

"my $_" is relatively newish, coming in in Perl 5.9, it appears.

"man perlsub" has a section on "When to Still Use local".
 
R

Randal L. Schwartz

Ben> That seems bizarre, to me. To *my* mind, $_ belongs to some very small
Ben> block: a for or while (<>) loop a few lines long, or a map or grep
Ben> expression or block. These blocks might occur all over the place, but
Ben> they all set up their own (localised) values for $_, so they don't
Ben> affect each other.

The while *doesn't*. Maybe that's what bit you one day, so that you now
practice this "shake a voodoo stick" thing at it. In which case, you
can borrow my best practice on that and never use $_ for such loops:

while (my $line = <>) {
...
}

print "Just another Perl hacker,"; # the original
 
T

Tim McDaniel

That seems bizarre, to me.

To me as well. $_ was designed for repeated use as a temporary and to
be used in a number of string-processing functions. It's so
convenient, whether in a sub or elsewhere, to do

chomp;
s/\r+$//; # not sure this is needed in modern Perl
next if ! s/^\.\.\. //;
if (s/^Root // || s/^AltRoots\d+ //) {
push @roots, $_;
} elsif (my ($depot, $workspace) = s!^View\d+ (//depot/.*) (//.*)$!!) {
...
}

(That's code off the top of my head, a contrived example.) Of course
I could "my" a variable, but then I'd have to drop it in 6 different
places.
To *my* mind, $_ belongs to some very small block: a for or while
(<>) loop a few lines long, or a map or grep expression or
block. These blocks might occur all over the place, but they all set
up their own (localised) values for $_, so they don't affect each
other.

Citations:

"man perlsyn" says that a foreach loop always localizes its control
variable,

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.

The foreach statement modifier is described well above that, with
"(with $_ aliased to each item in turn)." but no statement at that
point about localization.

The perlfunc docs for map and grep refer to "locally setting $_ to
each element", so I presume that means localizing in the same sense.

Testing shows that they are all indeed localized in the same way:

#! /usr/bin/perl
use strict;
use warnings;
my @names = (' in a loop: wilma', ' barney', ' bambam');
$_ = "\nfred\n";
print;
foreach (@names) { print }
print;
print foreach @names;
print;
map { print } @names;
print;
print grep { 1 } @names;
print;
exit 0;

produces

$ perl local/test/045.pl

fred
in a loop: wilma barney bambam
fred
in a loop: wilma barney bambam
fred
in a loop: wilma barney bambam
fred
in a loop: wilma barney bambam
fred

(That was in Perl 5.8.8, but that's not the sort of thing Perl tends
to change, for fear of breaking code.) (I've never before used
"print" with no arguments, implicitly printing $_. It feels weird.)

But "man perlop" says (in 5.8.8)

Ordinarily you must assign the returned value to a variable, but
there is one situation where an automatic assignment happens. If
and only if the input symbol is the only thing inside the
conditional of a "while" statement (even if disguised as a
"for(;;)" loop), the value is automatically assigned to the global
variable $_, destroying whatever was there previously. (This may
seem like an odd thing to you, but you'll use the construct in
almost every Perl script you write.) The $_ variable is not
implicitly localized. You'll have to put a "local $_;" before the
loop if you want that to happen.

(BTW: "while <>" as a statement modifier works the same way as "while
(<>)" as a loop. I wasn't sure.)

Are there other cases of setting $_ implicitly? I can't think of any.

The while(<>) is an illustration of a silent bug.

And I don't know that "local $_" should be slow, given that every map,
grep, and foreach() has to do it.
I wouldn't ever use $_ as an ordinary variable, even in the main
program, except for oneliners with -n or -p.

If nothing else, it's not a descriptive variable name.
 
T

Tim McDaniel

Ben> That seems bizarre, to me. To *my* mind, $_ belongs to some very small
Ben> block: a for or while (<>) loop a few lines long, or a map or grep
Ben> expression or block. These blocks might occur all over the place, but
Ben> they all set up their own (localised) values for $_, so they don't
Ben> affect each other.

The while *doesn't*. Maybe that's what bit you one day, so that you
now practice this "shake a voodoo stick" thing at it. In which case,
you can borrow my best practice on that and never use $_ for such
loops:

while (my $line = <>) {
...
}

$ perl -e 'while (my $line = <>) { print $line }'
hello
hello
world
world
about to enter a null line
about to enter a null line


Huh. Right. Newline counts as a character,
Huh. Right. Newline counts as a character,
so the boolean test will not exit the loop when it sees it.
so the boolean test will not exit the loop when it sees it.
It does look cleaner than while (defined(my $line = <>))
It does look cleaner than while (defined(my $line = <>))
^D

But "$line =~" left, right, and center isn't as convenient as implicit $_.

I don't have any mental model of where my() and local() can legally be
placed, so I have to test it:

#! /usr/bin/perl
use strict;
use warnings;
$_ = "fred\n";
while (local $_ = <>) { s/^/ /; print; }
print;
exit 0;

produces (with suitable input):

$ perl local/test/046.pl
hello
hello
world
world
^D
fred

So I guess I'll use
while (local $_ = <>)

Or, I suppose
while (my $_ = <>)
after every place I use Perl upgrades past 5.9.? or whatever it is that
first allowed "my $_".
 
P

Peter J. Holzer

Ben> That seems bizarre, to me. To *my* mind, $_ belongs to some very small
Ben> block: a for or while (<>) loop a few lines long, or a map or grep
Ben> expression or block. These blocks might occur all over the place, but
Ben> they all set up their own (localised) values for $_, so they don't
Ben> affect each other.

The while *doesn't*. Maybe that's what bit you one day, so that you now
practice this "shake a voodoo stick" thing at it.

Uhm. I don't think jaialai was talking about Ben's code.

hp
 
T

Tim McDaniel

Quoth (e-mail address removed):

Be a little careful with 'my $_'. It currently breaks things like
List::Util::first that call callbacks with a localised global $_.

Hrm?

first BLOCK LIST

Similar to "grep" in that it evaluates BLOCK setting $_ to
each element of LIST in turn. "first" returns the first
element where the result from BLOCK is a true value. If BLOCK
never returns true or LIST was empty then "undef" is returned.

$foo = first { defined($_) } @list
# first defined value in @list
$foo = first { $_ > $value } @list
# first value in @list which is greater than $value

So if I do them within the scope of "my $_", the $_ in the block I
provide ("$_ > $value" or whatever) will resolve to the lexical, not
to the global?

http://stackoverflow.com/questions/3393038/does-my-do-anything-if-is-implied
Evan Carroll (I think) quoted from "perldoc perl591delta" that

In a scope where $_ has been lexicalized, you can still have
access to the global version of $_ by using $::_, or, more simply,
by overriding the lexical declaration with "our $_".

After "our $_", note that $_ refers to the same global as was
available outside the lexical "my $_". You can use
local our $_ = EXPR;
to get the package variable with its own localized value.

http://www.effectiveperlprogramming.com/blog/1333 goes farther, to say
that lexical $_ is a bug and given() is unusable. I didn't read it in
any detail to know whether he's saying something useful or incorrect.
 
J

jl_post

Voodoo.  It was needed in one place, but he does it everywhere now out
of fear, rather than thinking about it.


I know what you mean.

I once saw a Perl script that had "use Win32;" at the top, yet
nothing about the script was Windows-specific. Confused, I asked the
author of the script why he put that in. (I explained to him that
because of that line, the program could only be run on Windows
platforms, even though there was nothing in the script (besides that
line) that required a Windows operating system.)

He replied that he once saw the line "use Win32;" in a Perl script
that worked, so to be safe, he used it in all his Perl scripts.

I explained to him that it didn't need to be there, but of course
that made no difference to him. He just felt safer with it.

To me, that's an example of "Cargo-cult programming": "I don't
know what it does, but a working program had it, so it must be good.
So code that has it must be better than code that doesn't."
 
J

jaialai.technology

Uhm. I don't think jaialai was talking about Ben's code.

That is correct. I can affirm that I have never worked with Ben. :)
This was a very enlightening thread an I thank everyone that responded.
At the end of the day Randal is correct, I believe. The author of the
code in question was just blindly adding that line for more or less
uninformed and superstitious reasons.
 
T

Tim McDaniel

This thread was about a project where every sub was started with
local($_);

At the end of the day Randal is correct, I believe. The author of the
code in question was just blindly adding that line for more or less
uninformed and superstitious reasons.

I'll disagree, because what's also consistent with the evidence is
"better safe than sorry, prevent an accidental while(<$sth>) or other
place here stomping $_".
 
T

Ted Zlatanov

On Tue, 3 Apr 2012 21:41:44 +0000 (UTC) (e-mail address removed) (Tim McDaniel) wrote:

TM> This thread was about a project where every sub was started with
TM> local($_);


TM> I'll disagree, because what's also consistent with the evidence is
TM> "better safe than sorry, prevent an accidental while(<$sth>) or other
TM> place here stomping $_".

My personal approach to $_ is very simple. It follows from two of the
fundamental laws of programming: Know What The Hell Is Going On; and
Life's Too Short To Chase Bugs You Could Have Avoided.

If I know where $_ came from and can *see* the lines of code that
generated $_, I use $_. So a tight loop or a small block is OK. Never,
ever would I use $_ inside a function, hoping it's OK. Regardless of
the optimization, it's just begging a bug report later.

If I don't know if it may have changed, I switch to using a variable.
It's very important not to be lazy about this switch. Usually for me it
happens when the loop or code block exceeds 3 lines or calls a function.

I thus never have to use "local $_" because I KWTHISGO (hopefully, I say).

And I choose to avoid relying on $_ because LTSTCBYCHA.

Hope that helps...
Ted
 
R

Rainer Weikusat

I'll disagree, because what's also consistent with the evidence is
"better safe than sorry, prevent an accidental while(<$sth>) or other
place here stomping $_".

If you don't know what the code at deeper levels in the call tree is
going to do, how can you be sure that it won't intentionally try to
change 'the global $_'? It is already a bad idea to add code to work
around real bugs in other code and fixing these is always
preferrable (the long-term cost of 'a fucking mess' is usually severly
underestimated when being compared it with the short term cost of
'adding another quick & dirty hack, no time to solve the actual
problem now' ...). It is a really bad idea to add technically useless
code to work around hypothetical errors in other code.
 
T

Tim McDaniel

If you don't know what the code at deeper levels in the call tree is
going to do, how can you be sure that it won't intentionally try to
change 'the global $_'?

Indeed it could, and thank you for pointing it out. If it is possible
for a callee to change a value in a caller, one of the two should save
values and restore them at the end. For example, in the IBM
System/370 architecture, a subroutine caller must set four registers,
but the callee is responsible for saving and restoring all other
registers.

It's a lot more practical for a callee to protect $_, because it
happens once at the top of the sub instead of every caller potentially
having to protect $_ on every call it makes in a $_-using scope.
I've been assuming that subs preserve $_. Then again, my orkplace
uses the convention that sub names that start with an underscore are
supposed to be private and not called outside the source file, but
I've seen it happen nonetheless.

I'm thinking that lexical $_ is looking better and better, even with
the problem with map {} and grep {}.
 
P

Peter J. Holzer

If you don't know what the code at deeper levels in the call tree is
going to do, how can you be sure that it won't intentionally try to
change 'the global $_'?

You don't. The local($_) doesn't protect this function from its callees,
it protects the the callers from this function. It promises to the
caller that it won't modify $_.

hp
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top