On Saturday 12 July 2008 01:07:20 chromatic wrote:
> Obviously all Closures need valid captured contexts, but we have a big
> problem when attempting to invoke a named Closure before attaching its
> captured context. In Perl 5 terms, this is the relevant code:
>
> {
> my $x;
>
> sub set_x { $x = shift }
> sub get_x { return $x }
> }
Because Patrick asked, here's now Perl 5 handles it.
At compile time, Perl resolves all of the various lexical scopes and creates
lexpads. Each function gets compiled into pp_codes, but internally Perl also
stores each function as a data structure called a CV. A CV contains an array
of lexpads (think recursion).
As an optimization, all lexical access gets resolved from symbolic lookup to
indexed lookup in the appropriate lexpad. It's an optimization, but it
should help you understand some of the reasoning.
In the case of this example, the CVs for set_x and get_x both point to the
same (outer) lexpad for $x, and (to the best of my knowledge this is
completely true) all of the pp_codes which access $x know which lexpad and
which slot in the lexpad to use when accessing $x.
If you modify the code somewhat:
# point A
say get_x();
my $xr = get_x_ref();
say $$xr;
{
my $x = 1;
sub set_x { $x = shift }
sub get_x { return $x }
sub get_x_ref { return \$x }
}
# point B
say get_x();
say $$xr;
... then things are a little trickier, depending on when you call the named
closures. Perl 5 executes code that it compiles as if all code outside of
functions were in an anonymous compilation unit marked :load. At point A, $x
is undefined because 'my $x = 1' hasn't executed yet. At point B, it has.
Yet pay attention to get_x_ref() closely -- at both points, the lexical
environment is the same and $x refers to the same variable.
Now consider the case of an anonymous closure:
sub make_counter
{
my $start = shift;
return sub { $start++ }
}
In this case, the lexical binding doesn't occur at compile time. It occurs on
execution, specifically when Perl 5 executes the anoncode pp_code, which
grabs the CV for the anonymous function, clones it, and stores
make_counter()'s currently active lexpad (remember, an array of lexpads
attached to the CV so that recursion works -- there's no external context
structure governing Perl 5's internal calling conventions) as the clone's CV.
-- c