While tidying up some code recently I was pondering the problems with the use of the _method_name() convention to indicate private methods in Perl.
Of course their are many alternative conventions that prevent the accidental overriding of methods in subclasses, for example:
package Foo; my $_private = sub { ... }; my _private = sub { ... }; sub foo { my $self = shift; # we can specify the full package name $self->Foo::_private(); # we can call as a subroutine _private($self); # we can use lexically scoped subs $self->$_private(); };
They also all have disadvantages of one sort or another, for example:
With the package name and subroutine calling methods we can prevent the method from being inherited by subclasses by putting it in a seperate package (see (tye)Re: Private Class Methods for an example). So we can do things like:
package Foo; sub Foo::private::method { ... }; sub foo { my $self = shift; $self->Foo::private::method { ... }; };
Which is nice, but we still have to repeat the Foo::private package every time we call the method - which for VeryLongPackageNames could be tedious.
A convenient shortcut to the private method space would be nice. Maybe something like:
sub Foo::MY::method { ... }; sub foo { my $self = shift; $self->MY::method(); };
This fits in quite nicely with SUPER:: and NEXT::.
This is actually pretty trivial to implement - just stick an AUTOLOAD in the MY package:
package MY; sub AUTOLOAD { my $self = shift; our $AUTOLOAD; my $method = caller() . "::$AUTOLOAD"; $self->$method(@_); };
and Bob's the parental sibling of your choice.
Unfortunately this adds an extra method call worth of overhead to every private method. Probably a bad move - Perl's method invocation is slow enough as it is.
It would also be nice to be able to do:
sub MY::method { ... };
rather than
sub MyLongPackageName::MY::method { ... };
Having to repeat MyLongPackageName for every private method definition is a pain.
Looks like a job for (and I don't say this very often because they're evil :-) a source filter.
package MY; use strict; use warnings; my $Imported_from; sub import { $Imported_from = caller }; use Filter::Simple; FILTER_ONLY code => sub { s/(\w+::)*MY::/${Imported_from}::MY::/gs }; 1;
Which will allow us to write code like this:
package Foo; use MY; sub new { bless {}, shift }; sub hello { my $self = shift; $self->MY::greet_world("hi"); }; sub MY::greet_world { my ($self, $greeting) = @_; print "$greeting world\n"; }; package Bar; use base qw(Foo); use MY; sub new { my $class = shift; $class->MY::greet_world(); return $class->SUPER::new(@_); }; sub MY::greet_world { print "A new Bar has entered the world\n"; };
Without the two private greet_world() methods of Foo and Bar interfering, and without any run-time overhead.
Neat.
Worth throwing at CPAN?
In reply to Private method variations by adrianh
For: | Use: | ||
& | & | ||
< | < | ||
> | > | ||
[ | [ | ||
] | ] |