Perl modules loading, calling or returning

thibaultduponchelle

Tib

Posted on May 11, 2021

Perl modules loading, calling or returning

Modules (and CPAN) are one of the "killer feature" of Perl, this article will discuss module loading/calling and some subtleties around this topic.

Where modules live do not dictate how they are called.

It is something important to understand early when you learn Perl programming, how you will load a module does not dictate its future package name (how it will be called).

Let's start with a simple example with a empty module stored in a File.pm in a subdirectory:

Directory
└── Directory
    └── File.pm
Enter fullscreen mode Exit fullscreen mode

File.pm contains nothing but a true return value:

1;
Enter fullscreen mode Exit fullscreen mode

The import is done by pointing to the path of the pm file:

#!/usr/bin/env perl

use Directory::Directory::File;
Enter fullscreen mode Exit fullscreen mode

So far, all good! :D

But it is too simple, so let's continue with a more real world example that actually calls something:

package Directory::Directory::File;

sub foobar() {
        print "Foo Bar!\n";
}

1;
Enter fullscreen mode Exit fullscreen mode

It is imported and called like the following:

#!/usr/bin/env perl

# Import 
use Directory::Directory::File;

# Call
Directory::Directory::File::foobar();
Enter fullscreen mode Exit fullscreen mode

Notice that Directory::Directory::File::foobar(); matches the package declaration in the module file... Where actually when calling the modules functions/variables, we have to follow the package name decided by the author for its module (read the doc).

It could be Directory::Directory::File::foobar() or something totally different from the path.

Look at this new example:

Directory
└── Directory
    └── File.pm
Enter fullscreen mode Exit fullscreen mode

The module is stored in Directory/Directory/File.pm but belongs to package "Misleading" and declares/defines once again a function foobar:

package Misleading;

sub foobar() {
        print "Foo Bar!\n";
}

1;
Enter fullscreen mode Exit fullscreen mode

On importer side, the use and later the function call are not using the same "id" name:

#!/usr/bin/env perl

# Import 
use Directory::Directory::File;

# But package is actually named "Misleading" !!!
Misleading::foobar();

# WON'T WORK!
# Directory::Directory::File::foobar();
Enter fullscreen mode Exit fullscreen mode

N.B.: Please note that the good practice is to make match path and package name. But you can meet aliases

use vs require

The use and require are doing almost the same thing but at different phases.

The require documentation ends with "For a yet-more-powerful import facility, see use and perlmod."

The use keyword executes at compilation phase means before execution even if it's the last line of your program.

Here is my sample "Bazinga" module:

package Bazinga;

sub joke() {
    print "Bazinga!\n";
}

1;
Enter fullscreen mode Exit fullscreen mode

And trying to call the function joke before to actually load it works very well:

$ perl -I. -e 'Bazinga::joke(); use Bazinga;'
Bazinga!
Enter fullscreen mode Exit fullscreen mode

It would not be the same with imports at runtime.

$ perl -I. -e 'Bazinga::joke(); require Bazinga;'
Undefined subroutine &Bazinga::joke called at -e line 1.
Enter fullscreen mode Exit fullscreen mode

With runtime imports, I can also import conditionally a module.

The following example will reload a module:

require Bazinga;

joke();

# Is it already loaded?
if ($INC{"Bazinga.pm"}) {
    print "Press ENTER to reload module:";
    <STDIN>;
    delete $INC{"Bazinga.pm"}; # Remove it from list
    require Bazinga; # Reload it
    joke();
};
Enter fullscreen mode Exit fullscreen mode

When you run this code, it will import and execute joke then reload the module on "Enter" (and run joke again).
Since it reloads the module, I can even change the module (the content of the joke function) during the execution of the program:

sub joke() {
    print "Knock Knock Knock Penny!\n";
    print "Knock Knock Knock Penny!\n";
    print "Knock Knock Knock Penny!\n";
}

1;
Enter fullscreen mode Exit fullscreen mode

And everything becomes more dynamic:

Bazinga!
Press ENTER to reload module:    
Knock Knock Knock Penny!
Knock Knock Knock Penny!
Knock Knock Knock Penny!
Enter fullscreen mode Exit fullscreen mode

Perl module return value

Modules have to return a "true" value, it sometimes confuse beginners, but I read somewhere that it comes from Perl 4 where importing a module was much more like executing a subscript.

So what if we try to return a "false" value?

0;
Enter fullscreen mode Exit fullscreen mode

When importing it, we get in trouble:

$ perl -I. -e 'use Boom;'
Boom.pm did not return a true value at -e line.
BEGIN failed--compilation aborted at -e line 1.
Enter fullscreen mode Exit fullscreen mode

Why are there is 2 messages here?
There is one about importing and one about compilation because of the "phases" I mentioned just above.

Just replace use per require to change the phase where the import is processed and it will now only print the import failure:

$ perl -I. -e 'require Boom;'
Boom.pm did not return a true value at -e line 1.
Enter fullscreen mode Exit fullscreen mode

Some people are playing with modules return codes like Michael G Schwern see his stackoverflow response or more recently PEVANS with a 0x55AA in its Faster::Math

You can find more example of return values thanks to Acme::ReturnValue

-M

You can import a module with command line switch -M like the following:

$ perl -I. -MBazinga -e 'joke()'
Bazinga!
Enter fullscreen mode Exit fullscreen mode

Visibility

Namespace pollution and symbols export is an important topic that I will not discuss today but I leave you in a good company 😀

💖 💪 🙅 🚩
thibaultduponchelle
Tib

Posted on May 11, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related