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

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.