Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Perl Privvies

by tlm (Prior)
on Jul 16, 2006 at 18:12 UTC ( [id://561584]=perlmeditation: print w/replies, xml ) Need Help??

Dear brethren,

Perl's OO model famously (or notoriously) provides no support for private methods. One common way to achieve method privacy is to use "lexical methods":

use strict; use warnings; { package Foo; my $private = sub { my $self = shift; print "From $self: @_\n" }; sub public { my $self = shift; $self->$private( @_ ); } } ( bless [], 'Foo' )->public( 'Howdy!' ); __END__ From Foo=ARRAY(0x816cc20): Howdy!

This enforces privacy, all right! If anything it does it too well, since $private is not accessible even to external test programs. But then, something similar is true for private methods in other languages...

Anyway, here's an alternative that is less watertight but offers greater flexibility:

use strict; use warnings; { package Foo; sub restricted { die "Calling this method from external code is STRONGLY deprecated +\n" unless __PACKAGE__ eq caller; my $self = shift; print "From $self: @_\n" }; sub public { my $self = shift; $self->restricted( @_ ); } } my $foo = bless [], 'Foo'; $foo->public( 'Howdy!' ); $foo->restricted( 'Hey wassup!' ); __END__ From Foo=ARRAY(0x816cc20): Howdy! Calling this method from external code is STRONGLY deprecated

Granted, it is relatively easy to circumvent this little subterfuge with another one; just replace the external call to restricted with the first line below:

{ package Foo; $foo->restricted( 'Hey wassup!' ); } __END__ From Foo=ARRAY(0x816cc20): Howdy! From Foo=ARRAY(0x816cc20): Hey wassup!

At this point the subterfuge race can escalate with

use strict; { package Foo; sub terfuge { die "DON'T YOU BE CALLIN' THIS HERE METHOD, DANG IT!!!\n" unless __FILE__ . __PACKAGE__ eq join '', ( caller )[ 1, 0 ]; my $self = shift; print "From $self: @_\n" }; sub public { my $self = shift; $self->terfuge( @_ ); } } 1;
use strict; use warnings; use Foo; my $foo = bless [], 'Foo'; $foo->public( 'Howdy!' ); { package Foo; $foo->terfuge( 'Hey wassup!' ); } __END__ From Foo=ARRAY(0x816cc20): Howdy! DON'T YOU BE CALLIN' THIS HERE METHOD, DANG IT!!!

But I figure that if one feels the need to push this approach this far one may as well stay with the lexical private method described earlier. I prefer the second-to-last version, even though it can be easily circumvented, or rather, because it is easy to circumvent (which is useful during testing). I see it more as an extension of the docs than as a programmatic way to enforce privacy.

I don't recall this caller-based technique to enforce privacy as being used much, which makes me wonder if there are problems with it that I'm missing. Of course, many people, myself included, often find that naming conventions (e.g. the trusty leading underscore) are sufficient reminder of which methods are not meant to be used externally. The simple rule is something like "don't call anything that is not documented". This rule works well most of the time, but it is too restrictive in some cases. For example, it is very helpful to be able to invoke private methods when testing the public ones. But, especially in larger projects, I find that some private methods are more dangerous to call externally than others, and as the complexity of naming schemes increases, it becomes easier to lose track of what the various conventions mean. In such situations, an explicit check for potentially dangerous usage seems in order.

As always, I eagerly await your thoughts and comments.

the lowliest monk

Replies are listed 'Best First'.
Re: Perl Privvies
by xdg (Monsignor) on Jul 17, 2006 at 02:35 UTC
    I don't recall this caller-based technique to enforce privacy as being used much,

    My personal opinion is that most people don't feel they "need" it. This is back to the question of "culture" vs "control". In my inside-out objects talk, I use the analogy of double-yellow lines on the road versus concrete lane barriers. (Apologies to non-US drivers -- I hope the analogy is still clear.)

    • Double-yellow line analogy: You really shouldn't drive in the wrong lane against the flow of traffic because you might get yourself or someone else killed. Of course, there's nothing to stop you except the law, which will prosecute you after the fact if you're caught and can't explain yourself well.

    • Concrete lane barrier analogy: Even if you had a really, really good reason to drive in the wrong lane, you can't. In fact, if traffic is backed up and you have an emergency in the car, you can't even make an illegal U-turn to go the other way to get back the way you came.

    My point is that Perl isn't really about the second way of doing things. And I suspect that many (most?) Perl programmers are sufficiently happy with the first that at most a simple caller() check is all they deem worthwhile. Anyone who really wants around it is going to find a way and there are diminishing (or even negative) returns in the time required to even try to stop them.

    Just for fun, if you really want to bend your brain in the "subterfuge race", don't forget about overriding CORE::GLOBAL::caller() or the "# line 43 Bar.pm" trick. (See the very end of perlsyn for the last one.) Even for "private" lexical subs, I bet one could get at them with PadWalker.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Really like that analogy.. explains it very clearly (which can often be difficult, especially when describing the situation to people who come from a Java (etc.) background).

        Thank you. I spent a while trying to come up with an analogy that didn't involve shotguns and living rooms or whatever is the commonly-cited analogy for encapsulation. That one never really made a lot of sense to me.

        -xdg

        Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      In the UK, as indeed in other European countries, double yellow lines are for parking restrictions, close to the gutter, by the pavement/sidewalk. Double white lines are the equivalent "do not cross" of your double yellows. We tend to use the term "crash barrier" for anything that generically stops you from driving over the central reservation.

      Good analogy though. Like it!

      --

      Oh Lord, won’t you burn me a Knoppix CD ?
      My friends all rate Windows, I must disagree.
      Your powers of persuasion will set them all free,
      So oh Lord, won’t you burn me a Knoppix CD ?
      (Missquoting Janis Joplin)

        Over here in Norway, we have double-yellows. That is to say, when there even are two lanes. Even on the main arteries through the fjord region, there are long stretches of single-lane twisties. I have great respect for the lorry drivers around here!
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Perl Privvies
by Aristotle (Chancellor) on Jul 16, 2006 at 19:13 UTC

    I like to write my code like so:

    package Some::Module; our $VERSION = '0.1'; our @ISA = qw( Super::Module ); package Some::Module::YeWhoEnters; sub pfoo { # ... } sub xbar { my ( $foo, $bar ) = @_; # ... my $baz = pfoo( $bar ); # ... } sub Some::Module::new { my $class = shift; # ... return xbar( $_ ); }

    This way, everything is in a private package by default, and things only get into the publically documented package only when explicitly declared to belong there. At the same time, file-level lexicals are in scope during all of the code, and I can call private functions without having to prefix them with fully qualified package names. This is decent encapsulation, since you cannot accidentally call private functions as methods on the blessed reference without tricks, and you cannot invoke them as functions without the ::YeWhoEnters moniker either.

    It’s a bit annoying that this setup breaks the ->SUPER::foo notation in Perl because that is based on the compile-time package of a method, but then I use SUPER nowadays anyway which makes that a non-issue.

    But note that it’s not really possible to make this work for private methods without defeating the encapsulation.

    Makeshifts last the longest.

      *Some::Module::YeWhoEnters::ISA = \@Some::Module::ISA;

      would allow ->SUPER::...() to work.

Re: Perl Privvies
by Herkum (Parson) on Jul 16, 2006 at 23:17 UTC

    I read in the new book, Perl Hacks, (Hack #43) Make Methods Really Private, which can allow access control.

    My personal preference is Class::Std, it implements PUBLIC, PRIVATE and PROTECTED attributes that offer functionality for methods like Java.

    I am sure that there are more than those two, though they are the ones that I know off the top of my head...

      You know, inside-out objects were the one thing I thought I really disagreed with in Damian's book. After all, with competent developers, it's easy to get folks to respect encapsulation and not do naughty things. Now I work at a new company and after looking at the code, I'm beginning to think that inside-out objects are a much better idea "for the masses" than I thought.

      Cheers,
      Ovid

      New address of my CGI Course.

        I'm beginning to think that inside-out objects are a much better idea "for the masses" than I thought.

        Glad to hear it :-)

        For me the problem isn't folk doing naughty things. Folk poking deserve all they get. The problem are the accidental interactions...

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (2)
As of 2024-04-25 06:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found