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