Hack 57. Name Your Anonymous Subroutines


Trade a little anonymity for expressivity.

Despite the apparently oxymoronic name, "named anonymous subroutines" are an undocumented feature of Perl. Originally described by "ysth" on Perl Monks, these are a wonderful feature.

Suppose your program merrily runs along with a carefree attitudebut then dies an ugly death:

Denominator must not be zero! at anon_subs.pl line 11       main::__ANON__(0) called at anon_subs.pl line 17

What the heck is main::__ANON__(0)? The answer may be somewhere in code such as:

use Carp; sub divide_by {     my $numerator = shift;     return sub     {         my $denominator = shift;         croak "Denominator must not be zero!" unless $denominator;         return $numerator / $denominator;     }; } my $seven_divided_by = divide_by(7); my $answer           = $seven_divided_by->(0);

In this toy example, it's easy to see the problem. However, what if you're generating a ton of those divide_by subroutines and sending them all throughout your code? What if you have a bunch of subroutines all generating subroutines (for example, if you've breathed too deeply the heady fumes of Mark Jason Dominus' Higher Order Perl book)? Having a bunch of subroutines named __ANON__ is very difficult to debug.

$seven_divided_by is effectively a curried version of divide_by( ). That is, it's a function that already has one of multiple arguments bound to it. There's a piece of random functional programming jargon to use to impress people.


The Hack

Creating an anonymous subroutine creates a glob named *__ANON__ in the current package. When caller( ) and the rest of Perl's guts look for names for anonymous subroutines, they look there. Using carp and croak will quickly reveal this.

The solution is therefore to override this name temporarily. The easy way is to have the parent subroutine name the anonymous one:

sub divide_by {     my $numerator = shift;     my $name      = (caller(0))[3];     return sub     {         local *__ANON__ = "__ANON__$name";         my $denominator = shift;         croak "Denominator must not be zero!" unless $denominator;         return $numerator / $denominator;     }; }

Running the program now produces the output:

Denominator must not be zero! at anon_subs.pl line 12       __ANON__main::divide_by(0) called at anon_subs.pl line 18

Hacking the Hack

While that's better and it may fit your needs, it's not the most flexible solution. If you create several anonymous subroutines, they will all have the same name. It's more powerful to name the anonymous subroutines by passing the creator subroutine a nameor taking it from an argument, as appropriate.

use Carp; sub divide_by {     my ($name, $numerator) = @_;     return sub     {         local *__ANON__ = "__ANON__$name";         my $denominator = shift;         croak "Denominator must not be zero!" unless $denominator;         return $numerator / $denominator;     }; } my $three_divided_by = divide_by( 'divide_by_three', 3 ); my $answer           = $three_divided_by->(0);

The output looks like you expect:

Denominator must not be zero! at anon_subs.pl line 12       __ANON__main::divide_by_three(0) called at anon_subs.pl line 18                             

Note that this code as written does not work under the debugger. The solution is to disable a debugger flag before Perl compiles the anonymous subroutines:

my $old_p; BEGIN { $old_p = $^P; $^P &= ~0x200; } sub divide_by {      # ... } BEGIN { $^P = $old_p; } 

See perldoc perlvar for an explanation of $^P.




Perl Hacks
Perl Hacks: Tips & Tools for Programming, Debugging, and Surviving
ISBN: 0596526741
EAN: 2147483647
Year: 2004
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net