AlexTheBird
AlexTheBird

Reputation: 677

Is it a good practice in Perl when instance methods call each other over a "$self" reference?

Should instance methods in Perl call each other like this:

package BlaBla;

sub foo {
    my($self) = @_;
    #do something awesome;
}

sub bar {
    my($self) = @_;
    foo();
}

Or like this:

package BlaBla;

sub foo {
    my($self) = @_;
    #do something awesome
}

sub bar {
    my($self) = @_;
    $self->foo();
}

Thank you all for your time.

Upvotes: 5

Views: 674

Answers (5)

David Hammen
David Hammen

Reputation: 33126

Th first approach will not work as written.

sub bar {
  my($self) = @_;
  foo();
}

Here you are explicitly invoking foo as a function with no arguments via that foo(); statement. This will construct an empty local @_ array. The first statement in foo extracts $self as the first element from this local @_, so $self is undefined with this mechanism.

You need to pass $self as an argument to foo. There are (at least) five ways to do this:

  1. Arrow notation: Invoke foo via $self->foo();
  2. Indirect notation: Invoke foo via foo $self;
  3. Normal subroutine invocation: Invoke foo via foo($self);
  4. Implied arguments: Invoke foo via foo $self; This option can only used if foo has been predeclared with a function prototype.
  5. Don't construct that local @_ array. Let foo piggyback on bar's @_ array. Invoke foo via &foo; Note that there are no parentheses in this invocation.

Regarding option #5: It does exist. Don't use it. It will get you into trouble.

The difference between #2 and #4 become apparent when foo takes additional arguments. The indirect notation becomes (for example) foo $self 42; while the implied form becomes foo $self, 42; The indirect notation (option 2) is now strongly discouraged. Options 3 and 4 are also discouraged for use with methods.

That leaves option #1. Even though the arrow notation is just syntactic sugar, it means a lot to the reader. Use it.

Upvotes: 3

Dave Sherohman
Dave Sherohman

Reputation: 46187

Yes, you should include $self - if you don't, polymorphism won't work because each method will look for all calls in its own package, even if they're overridden in a subclass. Worse, if the referenced method is inherited from a parent and not overridden in the current subclass, the program will die because it doesn't exist in that package. And that's even if you do remember to try to fake out Perl's OO mechanism by passing $self as the first parameter.

Upvotes: 15

Alex
Alex

Reputation: 5893

If foo() is a method that can be called from other places you should certainly always call it as a method, otherwise classes inheriting from you won't be able to override it, and you'll force your foo() to check if it's being called in a methody-way or in a functiony-way.

On the other hand, if foo() is just some helper code that a few of your methods use, and is quite specific to your implementation of the class, then you can call it directly, but should then really always call it directly, and might want to label it as a private function available to the class by prefixing its name with an underscore: _foo().

Upvotes: 9

Wooble
Wooble

Reputation: 89927

The first one won't work, assuming you use $self somewhere in foo(), because it's not getting any arguments passed. You could call foo($self), but you're almost certainly better off using actual method call syntax instead of throwing the OO features of Perl out the window and just treating foo() as a function that happens to call its first parameter "$self".

Upvotes: 8

Tamzin Blake
Tamzin Blake

Reputation: 2594

Yes, instance methods should call each other with $self - otherwise, in some cases you'll need to check the args of the method and figure out whether it's being called on $self or not, which will make your code messier for no reason.

Upvotes: 4

Related Questions