Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

RFC: Lexcically scoped methods, a toy example

by dcmertens (Beadle)
on Jun 11, 2013 at 21:25 UTC ( #1038345=perlmeditation: print w/ replies, xml ) Need Help??

PDL is a very general datatype. To one person, a 2D piddle may represent a collection of samples from a song while to another person a 2D piddle may represent an image. However, when you say use PDL::Image2D, various methods get installed into the PDL package, for all piddles to use. This naturally leads to the careful selection of long-winded names in a defensive approach to not getting your toes stepped on (or not stepping on somebody else's toes).

It occurred to me a few days ago that we could manage this with lexically scoped methods. I don't mean lexically scoped subroutines (i.e. Lexical::Sub) because I really like PDL's method-chaining and want to keep that. Nor do I mean lexically scoped methods that get called on ALL objects, regardless of type (i.e. Method::Lexical) because I only want to (ultimately) modify the PDL method resolution.

To achieve this, I wrote a simplistic but functional AUTOLOAD method that examines the hint hash to find mappings from method names to functions in other packages. Then, I wrote a pragma that modified the hints hash, adding a mapping. After a bit of confusion and fiddling, I finally got it to work. Here is an example to illustrate:

#lexical-methods-test.pl use strict; use warnings; use 5.010; use Foo; my $obj = Foo->new; # Look, we can call normal class methods: $obj->foo; # This will fail (which is why it's in an eval) eval { $obj->bar; 1; } or do { print "Unable to call 'bar' on Foo object in this lexical scope\n" +; }; { # Lexically allow method "bar" as a Foo method: use Bar; $obj->foo; $obj->bar; } # Out here, normal method calls still work $obj->foo; # Kaboom! $obj->bar;

Outside of the lexical scope where I say "use Bar", calling the bar method on the Foo object issues an exception. Installing the mapping is quite simple:

# Bar.pm use strict; use warnings; use 5.010; package Bar; # Installs a method called bar sub import { $^H{"Foo/bar"} = 'Bar/bar'; } sub unimport { delete $^H{"Foo/bar"}; } sub bar { print "Calling method bar from package Bar\n"; } 1;

Writing the class for which this works was a little trickier:

# Foo.pm use strict; use warnings; use 5.010; package Foo; use Carp 'croak'; sub new { my $class = shift; $class = ref($class) || $class; return bless {}, $class; } sub foo { print "Calling method foo from package Foo\n"; } sub AUTOLOAD { my $self = $_[0]; # Get the called method name and trim off the fully-qualified part ( my $method = our $AUTOLOAD ) =~ s{.*::}{}; # Get the hints hash for the calling lexical scope my $hinthash = (caller(0))[10]; if (exists $hinthash->{"Foo/$method"}) { my ($package, $package_method) = split '/', $hinthash->{"Foo/$method"}, 2; # Retrieve the subref and goto() it my $subref = $package->can($package_method) or croak("Lexically scoped Foo method $method points to a +nonexistent method ${package}::$package_method"); goto &$subref; } elsif (my $super_method = $self->SUPER::can($method)) { goto &$super_method; } elsif ($method eq 'DESTROY') { # Do nothing if we come to this. return; } else { croak("Can't locate object method \"$method\" via package \"" +. __PACKAGE__ . '"'); } } sub can { my ($self, $method) = @_; # Check if it's a lexical method my $hinthash = (caller(0))[10]; if (exists $hinthash->{"Foo/$method"}) { my ($package, $package_method) = split /\//, $hinthash->{"Foo/$method"}, 2; # Retrieve the subref and goto() it my $subref = $package->can($package_method) or croak("Lexically scoped Foo method $method points to a +nonexistent method ${package}::$package_method"); return $subref; } elsif (my $super_method = $self->SUPER::can($method)) { return $super_method; } else { return undef; } } 1;

I've created a gist with this code example on github if anybody would like to play with it. I know that getting AUTOLOAD to work correctly is tricky, at best, but I also know that PDL doesn't get much in the way of complicated class dependencies underneath it, and I think it is possible to get this to work well enough that PDL might be able to use this sort of thing.

Thoughts?

Comment on RFC: Lexcically scoped methods, a toy example
Select or Download Code

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://1038345]
Approved by Corion
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2015-07-06 10:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (72 votes), past polls