In article <8i1H!0
...@cs.psu.edu> f
...@cs.psu.edu (Felix Lee) writes:
: Re: local() statement inside a block,
:
: Okay. Here's a different explanation. A 'block' is a dynamic object
: that takes considerable effort to create. If a loop were to create
: and destroy a block on every iteration, it would be much, much slower,
: so instead, loops create their block just once and repeatedly execute
: it. (This is not really any different from how other languages treat
: blocks, except many languages have static block objects instead.)
This is essentially correct. I guess that's part of why you don't see
more interpreters for declarative-rich languages...
But my usage of terms will certainly seem perverse to those steeped in
compiler technology. And I do mis-speak myself. The book shouldn't
have said that local() "declares" anything.
As for the term "block", I only called it that because of it's
resemblance to the blocks of a compiled language. However, in Perl,
the only bit of static scoping tied to blocks is the package
declaration. (There is no scoping for subroutines or formats.) All
other scoping happens at runtime, in whichever way I deem to be most
useful and efficient. There's a heap of stuff internal to the
interpreter that gets localized just like your local variables, and
it's all dynamic. It almost has to be, if you want the compiler to run
in under 53 megabytes.
: Now this loop optimization interacts poorly with the fact that 'local'
: is an executable statement, not a declaration. In nearly every other
: language, the equivalent to local is a declaration, so it happens just
: once, when the block is created. But in perl, the local happens every
: time the block is executed.
:
: This is merely a flaw in implementation. It makes no difference to
: the meaning of the program whether a local() in a loop consumes
: megabytes of memory or not.
True, but there is a semantic distinction here too, quite aside from
the quibble about vocabulary. In the current setup
for (1..10) {
$huh = $foo;
local($foo);
...
}
sets $huh to the current local value of $foo. It only gets the global value
of $foo on the first time through the loop. If we made $foo revert
to the global value at the end of each iteration, $huh gets the global
value each time.
: One way to fix this is to turn local into a declaration, but this may
: break code like this:
: $x = 'local($y)';
: { eval $x; $y = 3; }
: Does this matter? Perhaps not.
Actually, the example above is ok, since local() is local to an eval too.
But it would definitely break things like
local($_) = $_[0] if @_;
: Another way to fix this is to note which block local variables belong
: to. If you execute another local for the same variable in the same
: block, you can reclaim the old version since it will never be
: accessible again. Or, equivalently, you could make the local a noop
: and just reuse the same slot.
But you have to be very careful about recursion. You don't want
sub FOO {
local($BAR);
...
&FOO;
}
to break. Nevertheless, it would be a good way to fix it if we want to
keep the current semantics. If we want local() to revert at the
end of the block, however, it won't fly.
Making it revert at the end of the block would certainly impose more
time overhead (and less space overhead, which is what started this all
off in the first place) on those scripts that do local() within a loop
block. It would probably also impose more overhead on scripts that don't
do local().
One thing that complicates matters is that the interpreter DOESN'T
actually exit the block on each loop iteration. A loop like
while ($cond) {
&stuff;
}
is optimized to something resembling
if ($cond) {
TOP:
&stuff;
last unless $cond;
goto TOP;
}
except that the if isn't really an if and the goto isn't really a goto.
The inner block is really a circular linked list of statements, so it
looks to the interpreter like
&stuff;
last unless $cond;
&stuff;
last unless $cond;
&stuff;
last unless $cond;
&stuff;
last unless $cond;
&stuff;
last unless $cond;
...
In order to make local() revert, I'd have to stick something in to
check if anything needs to revert, and that might slow things down
even when nothing needs to revert. Yes, compile-time analysis might
determine whether such a check was even necessary, but Perl already
does about 2 1/2 passes, and I already get enough complaints about
startup time.
We all agree on the necessity of compromise. We just can't agree on
when it's necessary to compromise.
Larry