http://www.perlmonks.org?node_id=200095

penguin has asked for the wisdom of the Perl Monks concerning the following question:

OO Perl is fantastic. I love it. It just makes sense. However, one thing is really bugging me. Is there an eligant way to make methods in perl really private?

Now hold on, before y'all linch me let me first state that I HAVE heard and AGREE WITH Larry's quote about perl, living rooms and shot guns. (For those who havn't seen it I'll paraphrase: Perl would prefer that you stay out of its living room because it's the polite thing to do, not because you have a shotgun.) I really just want to know if it can be done. (To my mind this ability would really ADD to perl's flexibility--and acceptance.)

I'd appreciate any input. Even if it involves linching me ;)

Replies are listed 'Best First'.
•Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by merlyn (Sage) on Sep 23, 2002 at 14:09 UTC
    Yes. There are quite a few variations of "privateness" documented in theDamian's excellent Object Oriented Perl. You can check the file and line number of caller, for example.

    However, the moment you do that, I would not want to maintain your code. Every attempt I've seen at privacy has made the code more unmaintainable.

    Just name your "private" methods with a leading underscore, and put "do not call outside of this class" in a comment. People will get the idea, and they deserve what they get when they violate it. Why is that so hard to understand?

    -- Randal L. Schwartz, Perl hacker

      I hear ya dude :) However, you never know when management or a client might ask you to do the ridiculous ;)
        However, you never know when management or a client might ask you to do the ridiculous ;)
        And it's your job to explain why not to do that.

        Don't cave. If you do, you end up increasing the cost of the software overall. It's bad ethics.

        If your boss/client is so pigheaded that they won't see your well-reasoned point, quit. Get another job.

        -- Randal L. Schwartz, Perl hacker

Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by Zaxo (Archbishop) on Sep 23, 2002 at 14:11 UTC

    Sure, closures, the same technique that's used to make other lexicals really private, can be used for anonymous subroutines:

    { my $private_method = { foo => sub { stuff }, bar => sub { mostuff }, }; sub public_method { my @args = @_; $private_method->{foo}(@args); $private_method->{bar}(@args); } }

    The references within public_method keep the lexical $private_method alive after the name has gone out of scope.

    After Compline,
    Zaxo

Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by broquaint (Abbot) on Sep 23, 2002 at 14:13 UTC
    The only way you can have truly private methods is through lexically scoped subrefs. This is because you can always fake being in another package with package. This doesn't bode well for inheritance, but as clintp suggests you can get around it. There's also miyagawa's wonderful Attribute::Protected, but that can be bypassed if you really want to.
    HTH

    _________
    broquaint

      And that only works until your attacker starts using magic (generally via B (something like (this is off the top of my head) B::svref_2object($obj)->PADLIST->ARRAY) to get access to your lexicals anyway. From what I can tell - if you can get a code ref to it then you can get it's lexicals. At that point there isn't any protection unless...

      Ok, maybe... if you fork your process and orphan your objects via circular references and maybe if you are doing threading (which I gather is mostly a perl 5.8ism) you can still keep running code in those objects in a different process. Yeah, that's the ticket. (keep in mind this is just speculation)

      Not that I think that would actually work but you have to go to some interesting lengths to keep your lexicals really, really private.

      Update On further consideration just forking the process and allowing the private stuff to fall out of scope in the original process is enough to hide your stuff. Now you just have to do some sort of IPC to communicate back to the mother ship. ;-)

        Ok, maybe... if you fork your process and orphan your objects via circular references and maybe if you are doing threading (which I gather is mostly a perl 5.8ism) you can still keep running code in those objects in a different process. Yeah, that's the ticket. (keep in mind this is just speculation)
        Then we'll just write perl modules to r00t your system. Once w3 0wn j00z, we'll go on to install loadable kernel modules to tinker in your process space directly. We'll use your lexicals from the inside out!

        :) for the humor-impaired

Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by clintp (Curate) on Sep 23, 2002 at 14:05 UTC
    One cheesy idea I've exploited: keep your methods as subrefs in a lexical hash in the module's file. Have AUTOLOAD take care of actually dispatching to the methods when they're called. The subs are unreachable outside of the module unless you pass a reference outside somehow.

    It can make your code hard to read (and subclass, and...), but if all you want is privacy...

Re: REALLY Private Methods in perl: Is Perl Flexible enough to be made Inflexible?
by dws (Chancellor) on Sep 23, 2002 at 17:23 UTC

    Is there an eligant way to make methods in perl really private?

    You're got some good answers above. Here's a different approach.

    You can get the delayed effect of private methods by adopting a simple convention: Start all private methods with an underscore, and teach people not to invoke these methods from outside of the class hierarchy1. To enforce this, it's a simple matter to spider your code base looking for references to private methods that are not preceded by "$self->".

    Having a script that traverses the code base looking for certain patterns can be very useful. In team settings, I've set up scripts to look for special code patterns (e.g., "TBD" or "FIXME" comments, correct copyright strings, etc.) The scripts ran every night, and produced a report (an HTML page) that we would check every morning. It also gave us an easy way to chart our code growth over time.

    The question you'll need to ask yourself is whether you can live with the delay. I've found that the convention was good enough.


    1 Purists will note that this really enforced "protected" methods.

      Great idea! Care to share your script?

      -sam

        Care to share your script?

        My ex-employers own the last ones I wrote, but they're pretty simple. Use File::Find to traverse a hierarchy, then ignore file types that aren't interesting (like .doc or .exe), and finally processing files that are. Stripped of extraneous detail, they look something like:

        use File::Find; my $ignore = '(?:\.exe|\.doc)$'; find(\&consider, $root); emit_report(); sub consider { my $path = $File::Find::path; return if $path =~ m/$ignore/o; process($path); }
        Processing consists of slurping the file into memory and running a set of regexs against it, recording which files match which regex. Then sort the hashes and spit out HTML. It's just bookkeeping at this poing. Being a potential memory pig isn't a serious issue for a script that runs once a day at 2am.

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
    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 localwithlocal3.pl #!/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:
      $ attributeprotectedwithlocal.pl 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 +thlocal.pl line 49.

    • A problem with Attribute::Protected is that the universal method "can" still see the methods qualified as private. 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".