Adam S
Adam S

Reputation: 9245

Perl: Exporting functions from a package which is in a subdirectory?

I've simplified my problem down to a small example. There is something happening that I don't understand with the way importing / exporting functions from packages works.

This code works and I can call greet() from use-myPack.pl.

# myPack.pm
package myPack;
require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = ('greet');

sub greet {
    printf("Hello!\n");
}

1;

# use-myPack.pl
use myPack qw(greet);
greet();

# Output
PS C:\Users\adam> perl .\use-myPack.pl
Hello!

However, when I call use-myPack.pl from the parent directory and use the :: operator to ensure it can still use myPack, it can't find the function called greet().

# myPack.pm
package myPack;
require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = ('greet');

sub greet {
    printf("Hello!\n");
}

1;

# use-myPack.pl
use adam::myPack qw(greet);
greet();

# Output
PS C:\Users> perl .\adam\use-myPack.pl
Undefined subroutine &main::greet called at .\adam\use-myPack.pl line 2.

Can anyone help explain why I can call greet() in the first case, but not the second?

Upvotes: 1

Views: 510

Answers (2)

Brad Gilbert
Brad Gilbert

Reputation: 34130

Here is a mini-tutorial about the differences between packages and modules.


use My::Package 1.0 (qw'some options');
#                  ↑ no comma

is effectively the same as:

BEGIN{
  # load the <module> if it isn't loaded already
  require My::Package;
  # or
  require 'My/Package.pm';



  # check the version of the <package>
  'My::Package'->VERSION(1.0); # compare against $My::Package::VERSION

  # do extra processing in the <package>
  'My::Package'->import(qw'some options');
}

Notice that only require dealt with the module, the rest of the statements all dealt with the package of the same name.

My/Package.pm:

package My::Package;
use strict;
use warnings;

our $VERSION = 1.0; # checked by UNIVERSAL::VERSION
# you can actually override VERSION. ( don't though )

# bad example of an import method
sub import{ # called by C<use>
  my( $package, $filename, $line ) = caller;
  print 'you called ', __PACKAGE__, " from $filename at line $line with options @_\n";

  # the following is similar to how Exporter::import works
  no strict 'refs';
  *{ $package.'::exported_sub' } = \&My::Package::Subs::exported_sub;
}

package My::Package::Subs; # <== look another package in the same module

sub exported_sub{...}

If you call use with an empty list then import is not called.

use My::Package ();
BEGIN{
  require 'My/Package.pm';
}

The main reason you would do this is to make sure that the module is loaded at compile time without it doing anything extra on behalf of the calling package.


When you do this:

require Exporter; # loads module named Exporter
@ISA = qw(Exporter); # inherit C<import> from Exporter.

Any method calls to import on the package will be handled by import in Exporter. This includes any use statements.
Provided of course that you don't override it by defining your own import or AUTOLOAD methods.



As an aside

:: is not an operator (otherwise it would be in perlop).
Instead :: and ' are called package separators.

If :: were an operator, you would be able to do A :: B :: C and A::(B::C), which are (currently) syntax errors.

Upvotes: 1

ikegami
ikegami

Reputation: 386676

The package name used in the use directive must match the package named used in the module's package directive (because you end up doing PACKAGE_NAME->import(IMPORT_LIST)).

use Foo::Bar;     with     package Foo::Bar;     = ok
use Bar;          with     package Foo::Bar;     = not ok
use Foo::Bar;     with     package Bar;          = not ok

The simplest option is to change the module to use

package adam::myPack;

to allow you to keep using

use adam::myPack qw( greet );

Otherwise, you need to switch to

use myPack qw( greet );

and make Perl find the module by changing the library search path (@INC) by using

use lib 'adam';

or

use FindBin qw( $RealBin );
use lib "$RealBin/adam";

The former works if the cwd is different than the scripts directory, while the latter doesn't.


Alternatives to use lib

If you called the directory lib instead of adam, you could use

use mylib;  # Short for something similar to:
            #   use FindBin qw( $RealBin );
            #   use lib "$RealBin/lib", "$RealBin/../lib";

Or if you have a directory to which you install module for all your scripts (e.g. ~/perl5/lib), set the environment variable PERL5LIB to it in your login script.

export PERL5LIB=~/perl5/lib     # sh & bash syntax

Upvotes: 1

Related Questions