jgrump2012
jgrump2012

Reputation: 397

Are there dangers to specifying the package name when making a local subroutine call?

I've inherited Perl code similar to what I have shown below. Package contains common subroutines and does not import nor export namespace. This calling convention seems atypical. Refactoring is an option.

I'm interested in understanding the risks/side effects of explicitly naming the package when calling a subroutine within the current package. Thanks in advance.

package Util;
sub _step1 {
    # <code>
}
sub _step2 {
    # <code>
}
sub doWork {
   Util::_step1();
   Util::_step2();
}
1;

Upvotes: 0

Views: 112

Answers (2)

David W.
David W.

Reputation: 107060

Interesting. I'm thinking how this would affect inheritance.

Imagine a child class of the Util class that overrides the _step1 and _step2 method. If that class called the dowork method. It would not call the child class' _step1 and _step2 methods, but the parent class' _step1 and _step2 method.

Addendum

Function calls ignore inheritance. The module would need to do something like Util->_step1() or package->_step1() for inheritance to matter, and even then the package search would start with Util, not a child class. – Ven'Tatsu

Really? Seems simple enough to test.

I have two packages: Local::Util and Local::Util::Child defined in my program. Local::Util::Child is a child class of Local::Util.

Class Local::Util has the following constructors and methods defined:

  • new: Create a new object of that class
  • _step1
  • _step2
  • doWork: This calls _step1 and _step2 with the Util:: prefix.
  • doWork2: This calls _step1 and _step2 without the Util:: prefix.
  • doWork3: This calls _step1 and _step2 with the __PACKAGE__ prefix.
  • doWork4: This called _step1 and _step2 with a $class prefix taken from ref.

Class Local::Util::Child only redefines the _step2 method.

Here's the program:

#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);

# This is our basic Local::Util object manipulation

my $util_obj = Local::Util->new;
say q("$util_obj" is a member of the ") . ref($util_obj) . q(" class);

print q($util_obj->_step1: );
$util_obj->_step1;

print q($util_obj->_step2: );
$util_obj->_step2;

# This is a child class object of the above

my $uc_obj = Local::Util::Child->new;
say q("$uc_obj" is a member of the ") . ref($uc_obj) . q(" class);

# Calls to straight forward methods

print q($uc_obj->_step1: );
$uc_obj->_step1;

print q($uc_obj->_step2: );
$uc_obj->_step2;

# Now calls to methods that call other methods

say qq(\n=====\$util_obj->doWork=====);
$util_obj->doWork;

say qq(\n=====\$uc_obj->doWork=====);
$uc_obj->doWork;

say qq(\n=====\$util_obj->doWork2=====);
$util_obj->doWork2;

say qq(\n=====\$uc_obj->doWork2=====);
$uc_obj->doWork2;

say qq(\n=====\$util_obj->doWork3=====);
$util_obj->doWork3;

say qq(\n=====\$uc_obj->doWork3=====);
$uc_obj->doWork3;

say qq(\n=====\$util_obj->doWork4=====);
$util_obj->doWork4;

say qq(\n=====\$uc_obj->doWork4=====);
$uc_obj->doWork4;

###################################################
# Package Local::Util
#
package Local::Util;

sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
}

sub _step1 {
    say "I'm calling Local::Util::_step1";
}

sub _step2 {
    say "I'm calling Local::Util::_step2";
}

sub doWork {
    Local::Util::_step1();
    Local::Util::_step2();
}

sub doWork2 {
    _step1();
    _step2();
}

sub doWork3 {
    __PACKAGE__->_step1();
    __PACKAGE__->_step2();
}

sub doWork4 {
    my $self = shift;
    my $class = ref $self;

    $class->_step1();
    $class->_step2();
}
#
#############################################

#############################################
# Package Local::Util::Child
#

package Local::Util::Child;
use base qw(Local::Util);

sub _step2 {
    say "I'm calling Local::Util::Child::_step2";
}

And, here's the output:

"$util_obj" is a member of the "Local::Util" class
$util_obj->_step1: I'm calling Local::Util::_step1
$util_obj->_step2: I'm calling Local::Util::_step2
"$uc_obj" is a member of the "Local::Util::Child" class
$uc_obj->_step1: I'm calling Local::Util::_step1
$uc_obj->_step2: I'm calling Local::Util::Child::_step2

=====$util_obj->doWork=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$uc_obj->doWork=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$util_obj->doWork2=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$uc_obj->doWork2=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$util_obj->doWork3=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$uc_obj->doWork3=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$util_obj->doWork4=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2

=====$uc_obj->doWork4=====
I'm calling Local::Util::_step1
I'm calling Local::Util::Child::_step2

Interesting. You're right that it makes no difference whether or not I put the Util:: prefix, it still calls the parent method and not the child's method. Putting the __PACKAGE__ prefix does the same (which I thought how you're suppose to do it in order to make sure you're calling the current class's definition). The only way I could get the child to work is to use the $class prefix which I had to get from the ref command.

So, it seems if you call a method in another method, it will default to that class's method and not the child's method. I guess this makes sense -- especially since it looks like _step1 and _step2 are private methods that shouldn't be accessed outside of the parent method.

Upvotes: 3

ikegami
ikegami

Reputation: 385996

That's fine. The only down side is that you might forget to rename the sub calls if you rename the module.

But it's an odd thing to want to do. If you tell us the reason you want to do this, we can perhaps provider a better way of doing it.

Upvotes: 0

Related Questions