Violate closure-based encapsulation when you really need to. Very few rules in Perl are inviolatenot even the rule that lexicals are inaccessible outside their scopes. For closures to work (and even lexicals in general), Perl has to be able to access them somehow. If you could use the same mechanism, you could read from and write to these variables. This is very useful for debugging closures and closure-based objects [Hack #43]. It's scary and wrong, but sometimes it's just what you need. The HackRobin Houston's PadWalker module helpfully encapsulates the necessary dark magic in a single place that, most importantly, you don't have to understand to use. Suppose you have a misbehaving counter closure:[7]
sub make_counter { my ($start, $end, $step) = @_; return sub { return if $start == $end; $start += $step; }; } One way to debug this is to throw test case after test case at it [Hack #53] until it fails and you can deduce and reproduce why. An easier approach is to show all of the enclosed values when you have a misbehaving counter. Once you have a counter, use PadWalker's closed_over( ) function to retrieve a hash of all closed-over variables, keyed on the name of the variable: use Data::Dumper; use PadWalker 'closed_over'; my $hundred_by_nines = make_counter( 0, 100, 9 ); while ( my $item = $hundred_by_nines->( ) ) { my $vars = closed_over( $hundred_by_nines ); warn Dumper( $vars ); } Running the HackRunning this reveals that $start, the current value of the counter, quickly exceeds 100. $VAR1 = { '$start' => \\9, '$step' => \\9, '$end' => \\100 }; $VAR1 = { '$start' => \\18, '$step' => \\9, '$end' => \\100 }; # ... $VAR1 = { '$start' => \\6966, '$step' => \\9, '$end' => \\100 }; # ... $step and $end are okay, but because $start never actually equals$end, the closure never returns its end marker. Changing the misbehaving operator to >= fixes this.[8]
Hacking the HackOne good turn of scary encapsulation-violation deserves another. The hash that closed_over( ) returns actually contains references to the closed-over variables as its values. If you dereference them, you can assign to them. Here's one way to debug the idea that the comparision operator is incorrect: while ( my $item = $hundred_by_nines->( ) ) { my $vars = closed_over( $hundred_by_nines ); my $start = $vars->{'$start'}; my $end = $vars->{'$start'}; my $step = $vars->{'$step'}; if ( $$start > $$step ) { $$start = $$end - $$step; } } PadWalker is good for accessing all sorts of lexicals. If you have a subroutine reference of any kind, you can see the names of the lexicals within that subroutinenot just any lexicals it closes over. You can't always get the values, though. They're only active if you're in something that that subroutine actually called somewhere. Be careful, though; just because you can look in someone's closet doesn't mean that you should.
|