Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?

by casiano (Pilgrim)
on Jan 18, 2008 at 13:41 UTC ( #663057=note: print w/replies, xml ) Need Help??

in reply to REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?

There is more than an answer to this question.

  • One way is to use a lexical reference to a method:

    pl@nereida:~/LEyapp/examples$ cat #!/usr/local/bin/perl -w use strict; { package Tutu; my $_tutu = sub { my $self = shift; "Inside tutu. My class is ".ref($self)."\n" }; sub plim { my $self = shift; print "plim can access _tutu as a private method:\n" .$self->$_tutu; } sub new { bless {}, $_[0]; } } package main; my $obj = Tutu->new(); $obj->plim();
    Now the $_tutu method is visible only inside the closure enclosing the Tutu package.
  • Most of the time the sort of privacy we need is to avoid a descendant class accidentally overwriting a private method. In such case, calling the method as a subroutine Class::_private_method($self) instead of using the $self->_private_method syntax will suffice.
  • You can have a sort of dynamic visibility of methods beteween classes in the same hierarchy (but only one level of inheritance) using the strategy of naming a lexical CODE reference inside the client methods
    #!/usr/local/bin/perl -w use strict; package Tutu; my $protected = sub { my $self = shift; "inside Tutu::tutu!\n" }; sub plim { my $self = shift; local *protected = $protected; print "Accessing 'protected' as a method: " .$self->protected(); } sub new { bless {}, $_[0]; } package SubTutu; our @ISA = qw{Tutu}; # SubTutu can overwrite 'protected' sub protected { my $self = shift; "inside overwritten tutu!\n" }; package main; my $obj = SubTutu->new(); $obj->plim(); print(Tutu->can('protected')? (Tutu::protected()."\n") : "main does not even know of 'Tutu::protected'\n" ); $obj->plim(); # Let us provoke an exception Tutu::protected();
    When executed gives the following output:
    $ Accessing 'protected' as a method: inside overwritten tutu! main does not even know of 'Tutu::protected' Accessing 'protected' as a method: inside overwritten tutu! Undefined subroutine &Tutu::protected called at ./attributeprotectedwi line 49.

  • A problem with Attribute::Protected is that the universal method "can" still see the methods qualified as private.
    When you run the following code:
    pl@nereida:~/LEyapp/examples$ cat #!/usr/local/bin/perl -w package SomeClass; use Attribute::Protected; sub foo : Public { } sub _bar : Private { } sub _baz : Protected { } sub another { my $self = shift; $self->foo; # OK $self->_bar; # OK $self->_baz; # OK } sub new { bless {}, $_[0] } package DerivedClass; @DerivedClass::ISA = qw(SomeClass); sub yetanother { my $self = shift; $self->foo; # OK $self->_bar; # NG: private method $self->_baz; # OK } package main; my $some = SomeClass->new; $some->foo; # OK print ($some->can('_bar')?"Yes, main can see that SomeClass has a _bar + method\n":"no\n");
    produces the following output:
    $ Yes, main can see that SomeClass has a _bar method
    But certainly the purpose of Attribute::Protected is to warn you as soon as possible of the crash rather than hide the method
  • Unfortunately -at the time of this writing - Sub::Lexical seems to be broken with modern versions of Perl. See the test results at CPAN
  • Let me tell you a story:

    I wrote Parse::Eyapp a LALR compiler compiler (s.t. similar to Parse::RecDescent). From a grammar specification produces an abstract syntax tree. The tree can then be manipulated using an attribute grammar like the one provided by Luke Palmer Language::AttributeGrammar.
    Language::AttributeGrammar was written with Parse::RecDescent in mind and assumes that node children are accesed by name instead than by ordinal number, which is the way used by Parse::Eyapp.
    Language::AttributeGrammar access to children is through a private method called _get_child.
    The fact that the private method wasn't really hidden allowed me to overwrite the method and to restore it after the modification so that if later in the program an attributed grammar - let us say for a Parse::RecDescent generated tree - needs the old version of _get_child it will work. See the pertinent fragment of code for a small calculator:

    94 my $attgram = new Language::AttributeGrammar <<'EOG'; 95 96 # Compute the expression 97 NUM: $/.val = { $/->{attr} } 98 TIMES: $/.val = { $<0>.val * $<1>.val } 99 PLUS: $/.val = { $<0>.val + $<1>.val } 100 MINUS: $/.val = { $<0>.val - $<1>.val } 101 UMINUS: $/.val = { -$<0>.val } 102 ASSIGN: $/.val = { $::s{$<0>->{attr}} = $<1>.val; $<1>.val } 103 EOG 104 105 { 106 # rewrite _get_child, save old version 107 no warnings 'redefine'; 108 *Language::AttributeGrammar::Parser::_get_child = sub { 109 my ($self, $child) = @_; 110 111 $self->child($child); 112 }; 113 114 my $res = $attgram->apply($t, 'val'); 115 } 116 # Restored old version of Language::AttributeGrammar::Parser::_ge +t_child
    Paraphrasing Larry Wall's quote, This is like going into the living room, changing a bit the things while none is using them, and cleaning and restoring them after use so that the next visitors will find them where they expect. If the living room were totally locked (I.e. if Luke Palmer decided to use a solution like the lexical closure reference above for _get_child) we couldn't do it.
    As someone else said, Perl's motto "Don't enter my private spaces unless invited to do so" carries an invisible caveat: "or you really need to".

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://663057]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (9)
As of 2021-06-21 16:32 GMT
Find Nodes?
    Voting Booth?
    What does the "s" stand for in "perls"? (Whence perls)

    Results (99 votes). Check out past polls.