To add some more confusion to what Yuval wrote:

In general, it doesn't seem to be very clear how inner (lexically)
subs see their enclosing lexical environments. Possibly I'm simply 
very confused, in which case un-confusing me would be much appreciated. 
Here're some code snippets.

{ 
  my $x = something();
  if $x==1 {
    ...code...
  }
}

In this case, when the inner block is entered from the outer as part
of execution, it's pretty clear how the inner block can see $x. There
can be many separate pads (local lexical environments) for the outer
block (for instance, if the outer block's a sub that recursed on itself)
with different values of $x, but only one of them was current just now
before we entered the inner block, and we can definitely arrange that
it be found as the inner block's ::OUTER.

With a closure, that works fine, too:

{ 
  my $x = something();
  return { $x++; }
}

As the inner block is cloned, the right pad for the outer block is
at hand, and $x can be copied from it (as a reference) to the
snapshot being built for the inner block.

But what about inner named subs?

{
  my $x = something();
  sub foo { $x; }
}

&foo is visible outside the outer block because it's a package variable.
Presumably at compile time the block of foo was compiled to a Code
object, and &foo in the current package now points to that Code object
(or actually the Routine object storing the Code object inside it, but
the difference doesn't seem to be relevant here?). All this happens
before runtime starts. Now we call foo() from somewhere far away. What's
it going to see as $x? Certainly not anything meaningful, because the
assignment to $x might not even have run by now (if it's a part of a sub
that was never called), or it may have been run in many versions.

So it seems safe to say that foo() should see undef here as the value 
of $x (perhaps because its idea of ::OUTER is really the compile-time
version of the outer pad). Now how about this:

{
  my $x = something();
  sub foo { $x; }
  foo();
}

Presumably in this case we do want foo() to see the right $x, current
at the time. How can it find that pad as its ::OUTER? 

More importantly, if in the example when foo() is called from far away
it sees $x as undef, how can the following work at all (I'm assuming
it should work)? At package-level:

my $x = 1;
sub foo {
  $x;
}

Now we call foo() from a different package and we expect it to see
$x==1. But how is the situation different? 

I thought I had an argument as to why it could be different: we could
say that a named sub's block is a closure like any other block, and it's
closed already at compile-time; at that time $x is captured by foo();
and since the file-level code is only run once, its runtime pad is the
same as its compil-time pad, therefore when $x is later assigned 1 at
runtime, it's the same Scalar object it was (with value undef) at 
compile-time, and foo() sees that change.

But now after re-reading S04 and S06 I see that's hopelessly muddled 
and wrong in at least two ways. First, S04 says that named subs don't 
clone anything until a reference is taken to them - until then they're 
just blocks of code. Second, S06 also says that the (file|package)-level 
code isn't necessarily run once, it's implicitly part of a &MAIN sub 
("The outermost routine at a file-scoped compilation unit is always
named &MAIN in the file's package."), unless &MAIN had been redefined;
and could in principle be called again. So that 
$x doesn't necessarily exist, at runtime, in just one pad. It seems that
the above is really something like

sub &MAIN {
  my $x = 1;
  sub foo {
    $x;
  }
}

But then it becomes _exactly_ identical to the case before where it
seemed inevitable that calling foo() from a faraway place doesn't let
it see $x==1. So how is this different here and how can it work?

Thanks,
Anatoly.

Reply via email to