Haloor
Haloor

Reputation: 221

How to organize private methods on Perl objects?

What is the correct way to handle methods which will not be called by the user? For example if the user calls a method, say do_stuff() from a driver script, and if do_stuff() relies on other subroutines, is it best practice to call those subs from within the do_stuff() method as follows:

sub do_stuff {
    my ( $self, %arg ) = @_;
    #does it things
    #and then calls the private sub as follows
    _private_sub( $self, %arg );
}

Upvotes: 4

Views: 524

Answers (4)

zdim
zdim

Reputation: 66944

If your do_stuff method needs the functionality that you decided should be kept "private" then by all means call _private_sub within it. That is precisely its purpose. Of course, having a method be "private" is a matter of convention as it cannot be enforced.

I'd like to mention another way, of using coderefs for private subs

my $_private = sub { ... };  # must be at least predeclared

sub do_stuff {
    my ($self, %arg) = @_;
    # ...
    my $from_private = $self->$_private(@args);    # or
    my $more_private = $_private->($self, @args);
}

The syntax is of the first call is odd but then that also warns that it is meant as a private facility. Note that this is a trick of sorts -- while $self is indeed passed as the first argument, one could use any old reference instead of $self and the call works and $_private is passed that.

Using this makes it harder for subclasses to inherit, thanks to mob for emphasizing this.

The second invocation uses $_private as a function rather than like a method, so we have to pass the object if it needs it. This further warns against its use. It clearly doesn't undergo any method lookup process (and is thus slightly faster), something to be well aware of.

I am a little uncertain as to what exactly the question seeks.

If it is about whether to use "private" subroutines in your code, then there is really no accepted best practice. They can be very useful, of course. But they are either not truly private or, if you hide them as lexicals that cannot be seen from outside the unit (scope) like here, their behavior differs so much that that itself is an issue. I use them, with due care.

A better approach is probably to use Moose or Moo, or some other module. The organizational conveniences may shift the focus so that issues of "privacy" never come up. Also, a variety of tools and features that come in the ecosystem surrounding them may make it easier to handle special needs. But these libraries do not provide any special magic that would provide "true" private methods ("private" like in some other languages), thanks to simbabque for emphasizing this.


Introduced in v5.18, and stable since 5.26, we have lexical subroutines. They are defined with my sub ... (or state) and exist only within their lexical scope. So surely nothing outside the file can see them. Go read up and play.

However, I find that one cannot use them via the method call syntax. I don't know about the design decisions and am not sure why that is, but one cannot do

# NOPE -- Can't locate object method "lexisubname" via package...
$self->lexisubname(...)  

unlike for a lexical coderef, which can be called via -> and the caller (a reference) is passed to it, as misleading as that may be. Unless I am missing something, that would restrict the use of lexical subs to a function call, where $self has to be passed in explicitly if needed.


Related:

There's got to be more similar pages discussing this.

Upvotes: 4

Dave Cross
Dave Cross

Reputation: 69314

All of the answers you have received so far say that it can't be done. But it can - in Perl 5.18 and later. See the section on lexical subroutines in perldoc perlsub.

Lexical Subroutines

WARNING: Lexical subroutines are still experimental. The feature may be modified or removed in future versions of Perl.

Lexical subroutines are only available under the use feature 'lexical_subs' pragma, which produces a warning unless the "experimental::lexical_subs" warnings category is disabled.

Beginning with Perl 5.18, you can declare a private subroutine with my or state. As with state variables, the state keyword is only available under use feature 'state' or use 5.010 or higher.

These subroutines are only visible within the block in which they are declared, and only after that declaration:

no warnings "experimental::lexical_subs";
use feature 'lexical_subs'; 

foo();              # calls the package/global subroutine

state sub foo {
  foo();            # also calls the package subroutine
}

foo();              # calls "state" sub
my $ref = \&foo;    # take a reference to "state" sub

my sub bar { ... }
bar();              # calls "my" sub

To use a lexical subroutine from inside the subroutine itself, you must predeclare it. The sub foo {...} subroutine definition syntax respects any previous my sub; or state sub; declaration.

my sub baz;         # predeclaration
sub baz {           # define the "my" sub
  baz();            # recursive call
}

Upvotes: 2

goug
goug

Reputation: 2444

To my knowledge, there's no "correct" way. Perl doesn't provide the ability to hide the functions, at least that I've ever run across. What I've done is use a naming standard where I start the internal-only function names with an underscore and clearly document that they're never to be called by external callers.

EDIT: The other answers triggered a thought. In each private method, you could check the results of the "caller ()" function and abort if the caller is anyone other than the local module. Honestly, I personally wouldn't go to the trouble, but if it's really important to you for some reason, that would emphasize the private nature of these methods.

Upvotes: 4

mob
mob

Reputation: 118665

As opposed to $self->_private_sub(...)? In my mind, the $obj->_method notation is always used with function that act on an instance of your class, and other kinds of calls are always used for other kinds of functions.

But if you're going to be paranoid, why not use an unconventional way of packing your arguments that will confound anyone that tries to use your private method in an object oriented way?

sub do_stuff {
    my ( $self, %arg ) = @_;
    ...
    _private_sub( %arg, $self );
}

sub _private_sub {
    my $self = pop;
    my %args = @_;
    ...
}

Upvotes: 0

Related Questions