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

Reply via email to