Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Real private methods using lexical subs in Perl 5.18.

by tobyink (Abbot)
on May 31, 2013 at 11:58 UTC ( #1036238=perlmeditation: print w/ replies, xml ) Need Help??

Perlers tend to use the convention of using a leading underscore to mark private/protected methods in OO code. However, this has some problems:

  • It conflates private and protected methods. Private methods being those methods which nobody outside the class has any business calling; protected methods being those which are OK for subclasses to call and potentially override.

  • When you're writing a subclass, you need to know about all the superclasses' private methods to ensure you don't accidentally override any of them by coincidentally writing a sub with the same name.

Perl 5.18's experimental lexical subs feature can be exploited to create truly private subs. We need to introduce a little syntactic sugar (or perhaps vinegar, depending on how you look at it), to circumvent the fact that lexical subs are not really designed to be called as methods - we use this syntax: $self->${\\&method_name}(@args). (Think of it as a mega-sigil.)

#!/usr/bin/env perl use v5.18; use feature qw(lexical_subs); no warnings qw(experimental); package MyClass { # # Private methods # my sub get_number { my $self = shift; return $self->{number}; } # # Public methods # sub new { my $class = shift; return bless {@_}, $class; } sub say_number { my $self = shift; # Slightly funky syntax for calling a # lexical sub as a private method... say $self->${ \\&get_number }; } } package ChildClass { use base "MyClass"; # # A public method that has the same name as a # private method in the superclass. Note that # the private method will *NOT* get overridden! # Yay! # sub get_number { warn "You're not allowed to get the number!"; } } my $o = ChildClass->new(number => 42); $o->say_number; # works... my $p = MyClass->new(number => 666); $p->get_number; # asplode; it's a private method

Update: I hasten to add that I'm not advocating doing this in production code any time in the near future. But it's an interesting option to think about in years to come.

I'll also point out that a similar thing can be achieved using coderefs in earlier versions of Perl:

my $private_method = sub { ...; }; $self->$private_method(@args);
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

Comment on Real private methods using lexical subs in Perl 5.18.
Select or Download Code
Re: Real private methods using lexical subs in Perl 5.18.
by Grimy (Monk) on Jun 01, 2013 at 02:10 UTC

    Yeah, I agree that relying on convention to mark 'private' subs is somewhat lackluster. This approach is really interesting.

    This is very interesting. I wonder why the ugly mega-sigil is required, though. I can't think of a good reason to disallow the $self->lexical_sub() syntax.
      > This is very interesting. I wonder why the ugly mega-sigil is required

      The mega-sigil is actually a workaround to call the lexical sub as a code-ref like described in the update.

      Keep in mind that for Perl the names in method calls are syntactically literal strings messaged to the object. And literal strings can't be scoped.

      I.a.W. Perl can't tell if you call $obj->private within the package/class or not. But generally allowing it fundamentally violates the definition of privacy.

      Calling $obj->$code_ref doesn't have this limitation, cause scalars can be lexically scoped.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

Re: Real private methods using lexical subs in Perl 5.18.
by LanX (Canon) on Jun 01, 2013 at 08:36 UTC
    > I'll also point out that a similar thing can be achieved using coderefs in earlier versions of Perl:

    > my $private_method = sub { ...; }; $self->$private_method(@args);

    still my preferred way to do it and I wonder why this approach is widely ignored.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      One reason is that
      my $private_method = sub { get_logger()->info('testing 1 2 3'); };
      wouldn't be able to report the method name.
        Unless Named anonymous subs helps you.
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Real private methods using lexical subs in Perl 5.18.
by tchrist (Pilgrim) on Jun 11, 2013 at 21:11 UTC
    Seems like a whole lot of work for something that can be quite reasonably enforced with one of these guards at the start of the method:
    if (caller ne __PACKAGE__) { confess "private method invoked" }
    And while there certainly are ways around that, anybody playing those games deserves whatever they get: they’re breaking the law, and it isn’t worth trying to stop them. You’re just trying to stop accidents not nefarious break-ins, for which this is plenty good enough.

    It also requires nothing more recent that v5.0 when Carp was introduced, nor lengthening the toolchain at all. There’s a lot to be said for simplicity.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2014-07-10 03:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (198 votes), past polls