This is an interesting Perl behaviour. (atleast to me :) )
I have two packages PACKAGE1
and PACKAGE2
which exports function with same name Method1()
.
As there will be so many packages which will export this same function, use
-ing everything in a Perl file will be tedious. So, I have created a general include file INCLUDES.pm
which will have these use
s.
INCLUDES.pm:
use PACKAGE1;
use PACKAGE2;
1;
PACKAGE1.pm:
package PACKAGE1;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw (
Method1
);
sub Method1{
print "PACKAGE1_Method1 \n";
}
1;
PACKAGE2.pm:
package PACKAGE2;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw (
Method1
);
sub Method1{
print "PACKAGE2_Method1 \n";
}
1;
Tests.pl:
##################first package################
package Test1;
use INCLUDES;
my @array = values(%INC);
print "@array \n";
Method1();
##################second package################
package Test2;
use INCLUDES; #do "INCLUDES.pm";
my @array = values(%INC);
print "@array \n";
Method1();
The motive is, only the latest package's Method1()
should be used in any Perl file.
The output surprises me.
I would expect that both Method1()
calls in Tests.pl
should be success.
But only the first Method1()
executes, the second Method1()
call says "undefined".
OUTPUT:
C:/Perl/site/lib/sitecustomize.pl PACKAGE1.pm C:/Perl/lib/Exporter.pm PACKAGE2
.pmINCLUDES.pm
PACKAGE2_Method1
C:/Perl/site/lib/sitecustomize.pl PACKAGE1.pm C:/Perl/lib/Exporter.pm PACKAGE2
.pm INCLUDES.pm
Undefined subroutine &Test2::Method1 called at C:\Temp\PackageSample\Tests.pl line 15.
Do somebody have any answers/views on this?
The actual scenario:
the methods in multiple Perl modules will be having same name. But the methods from the High preference perl module should only be used.
For example, if PACKAGE1
contains Method1(), Method2()
& PACKAGE2
contains only Method1()
, then Method1()
should be used from PACKAGE2
& Method2()
should be used from PACKAGE1
Basically I want to achieve a Hierarchy among modules based on Preference. Is there any way for this?
In Perl, use Module
is equivalent to
BEGIN { require Module; Module->import; }
But require
caches the list of modules that have been required. It only loads the module once per Perl process. So only the first use IMPORTS
does anything. Since your IMPORTS
module doesn't have an import
method, nothing happens when you use
it again.
I'm not quite sure what you're attempting to accomplish. Perhaps your IMPORTS module should be an actual package, with an import
method that exports whatever functions you want. That way, each use IMPORTS
would export functions into the package that called it.
use MyPackage
is equivalent to BEGIN{ require MyPackage; MyPackage->import }
. Inheriting from Exporter sets up an import
class method which does the function "aliasing".
The problem is that you INCLUDES modules does not re-export the modules correctly. This is important because this is the process that imports the functions into the caller namespaces. While this isn't hard to craft on your own, there is a handy module for this purpose Import::Into.
Here is an example contained within a single file, it should be easy enough to reinflate into multiple, the only important difference is in the Includes module. I have made some other superficial changes but those are more for my taste.
#!/usr/bin/env perl
use strict;
use warnings;
package PACKAGE1;
use parent 'Exporter';
our @EXPORT = qw(Method1);
sub Method1 {
print "PACKAGE1_Method1 \n";
}
package PACKAGE2;
use parent 'Exporter';
our @EXPORT = qw(Method1);
sub Method1 {
print "PACKAGE2_Method1 \n";
}
package Includes;
use Import::Into;
# uncomment in mulitple files
#use PACKAGE1 (); # prevent import
#use PACKAGE2 (); # ditto
sub import {
my $class = shift;
my $caller = caller;
PACKAGE1->import::into( $caller );
PACKAGE2->import::into( $caller );
}
package Test1;
Includes->import; # in seperate files replace with `use Includes;`
Method1();
package Test2;
Includes->import; # ditto
Method1();
A real world example is the module utf8::all
which makes extensive use of this mechanism to load lots of unicode stuff into the caller package.
Edit
To allow importing specific things from the Includes
module, you could have it inherit from Exporter
as well and craft its @EXPORT
and @EXPORT_OK
to do what you mean. Otherwise, you could continue with Import::Into
and make something like bundles.
sub import {
my $class = shift;
my $bundle = shift;
my $caller = caller;
if ($bundle eq 'Bundle1') {
PACKAGE1->import::into( $caller );
... # other things in Bundle1
} elsif ($bundle eq 'Bundle2') {
PACKAGE2->import::into( $caller );
... # other things in Bundle2
}
}
Then in your test modules
use Includes 'Bundle1';
In short crafting your own import
method is not that hard, and every little is magical about Exporter
. Once you learn about symbol table manipulation your don't need it or Import::Into
, though it is a slightly more advanced topic. Here is a question I asked about it much earlier in my Perl days: Demystifying the Perl glob (*)
All that said, if object-oriented concepts of inheritance and polymorphism will do the job, you might want to investigate that route too. Here is an example of that:
#!/usr/bin/env perl
use strict;
use warnings;
package PACKAGE1;
sub Method1 {
my $class = shift;
print "PACKAGE1_Method1 \n";
}
sub Method2 {
my $class = shift;
print "PACKAGE1_Method2 \n";
}
package PACKAGE2;
# if multiple files use this
#use parent 'PACKAGE1';
# rather than
our @ISA = 'PACKAGE1';
# any methods in PACKAGE2 will override those declared in PACKAGE1
sub Method1 {
my $class = shift;
print "PACKAGE2_Method1 \n";
}
package Test1;
# in seperate files need to use
#use PACKAGE2;
PACKAGE2->Method1();
PACKAGE2->Method2();
package Test2;
# ditto
#use PACKAGE1
#use PACKAGE2
PACKAGE2->Method1();
PACKAGE2->Method2();
# you can still use PACKAGE1 and get the originals
PACKAGE1->Method1();
PACKAGE1->Method2();
See now there is no Includes
package and no symbols are imported into the Test*
namespaces. PACKAGE2
provides Method2
because it inherits from PACKAGE1
and it does not override the method declaration with one of its own.