2

I have looked for the last few hours and I am stumped.

I did see How can I share variables between a base class and subclass in Perl? but it does not answer my question

I am trying to write a utility module (testing purposes at this point) for use in internally developed scripts. But I seem to be having a tough go at getting exporter to actually export what I need it to.

What I have is below:

lib/Internal.pm:

package Internal;

use     5.008_008;
use     strict;
use     warnings;
use     Exporter;

our $VERSION = 1;
our @EXPORT_OK = qw($EMPTY $NOW);
our %EXPORT_TAGS = ( ':all' => \@EXPORT_OK );

our $EMPTY = q{};
our $NOW = time;

1;

lib/Internal/Utility.pm:

package Internal::Utility;

use     5.008_008;
use     strict;
use     warnings;
use     FindBin::libs;
use     parent qw(Internal);
use     Data::Dumper;

our $VERSION = 1;
our @EXPORT_OK = qw(run);
our %EXPORT_TAGS = ( ':all' => \@EXPORT_OK );

sub run
{
    print Dumper $NOW;
    print Dumper $EMPTY;
    exit;
}

1;

bin/test.pl:

#!/usr/bin/env perl

use     5.008_008;
use     strict;
use     warnings;
use     FindBin::libs;
use     Internal::Utility qw(:all);

run();
exit;

When I run the test.pl, I get the below error:

Global symbol "$NOW" requires explicit package name at lib/Internal/Utility.pm line 8.
Global symbol "$EMPTY" requires explicit package name at lib/Internal/Utility.pm line 9.
Compilation failed in require at ./test.pl line 7.
BEGIN failed--compilation aborted at ./test.pl line 7.

Why is the use parent in Internal::Utility not importing the variables from Internal.pm? Is there a way to tell use parent to explicitly import some variable?

Speeddymon
  • 496
  • 2
  • 20

3 Answers3

4

In Perl, inheritance only affects how method calls are resolved. Everything else – variables, subroutines, and so on – remains just the same.

That means you should import those symbols into your derived class, just like you'd import from any other module:

package Internal::Utility;
...
use Internal ':all';
use parent qw(Internal);
...

Perl uses packages as its sole namespacing mechanisms, but packages can also be used as classes. This can be a bit confusing. As a general advice, you should never mix those two: a package should EITHER offer an object-oriented interface OR an exporter-based, procedural interface. Mixing the two tends to result in misleading designs.

amon
  • 57,091
  • 2
  • 89
  • 149
  • Perfect. This solved my problem, though I was also missing the `import` part from `use Exporter` as suggested (and needed apparently) by @zdim. I really appreciate the clarification on packages vs classes too and the advice as well. – Speeddymon Dec 13 '17 at 23:50
4

You are trying to "inherit", with use parent, in a module from a module. The parent says

Establish an ISA relationship with base classes at compile time

(my emphasis). It is a pragma to set up use of a base class in a derived one, and Internal and Internal::Utility are not classes really (they don't bless). See a note at the end.

Write them as classes. (Then you won't need to think about exporting symbols.) That will give you a whole lot more capability and control, too.


As for the Exporter, you have to say

use Exporter 'import';

so that the sub import is available to the module.

A sub named "import" is used to push symbols, that you specify in @EXPORT_OK etc, to the caller and thus must exist in a module. Or, quite nicely, a module may "borrow" it from Exporter, instead of implementing its own. But the Exporter doesn't export it by default.


An example with classes, showing a few ways to make constants available, along with a few other bits. When you choose one way (and remove code for others) and remove prints it gets far simpler; the longest Internal::Utility class shrinks to a few lines.

Base class Internal.pm

package Internal;

use warnings;
use strict;

# Set up constants for derived classes to use, for demo 
# BOTH via class data and method, and via an attribute

our $NOW = time;
sub get_NOW { return $NOW }

sub new { 
    my ($class, @pars) = @_;
    # Set up object's data ("attributes"), using @pars
    my $self = { _now => $NOW };  # ...
    # Make $self an object and this package a class
    return bless $self, $class;
}

sub other_methods { }

1;

Derived class (subclass), Internal/Utility.pm

package Internal::Utility;

use warnings;
use strict;
use feature 'say';

use parent 'Internal';  # now have everything from 'Internal' class

# Retrieve constant from parent, if you *must*
# It is much better to use a method or an attribute 
use mro; 
my $NOW = do {
    my $Parent = (__PACKAGE__)->mro::get_linear_isa->[1];
    my $now = $Parent . '::NOW';
    no strict 'refs';
    $$now;
};  
my $Now = $Internal::NOW;  # or use hard-coded parent name

# Constructor is also inherited (can change or override it altogether)

sub print_start_time {
    my $self = $_[0];
    say "\tIn ", __PACKAGE__, ". Start time:";
    say "\tObject's method (inherited): ", $self->get_NOW;
    say "\tObject's data (inherited):   ", $self->{_now};
    say "\tDirectly, as class method:   ", Internal->get_NOW;
    say "\tClass variable, from parent: $NOW";
    say "\tOther class variable:        $Now";
};
    
1;

Driver main.pl

use warnings;
use strict;
use feature 'say';

use Internal::Utility;

my $obj = Internal::Utility->new();

say "Created ", ref $obj, " object";

$obj->print_start_time();

say "class constant:   $Internal::NOW";
say "as object method: ", $obj->get_NOW;
say "as class method:  ", Internal->get_NOW;

This prints

Created Internal::Utility object
        In Internal::Utility. Start time:
        Object's data (inherited):   1513229709
        Object's method (inherited): 1513229709
        Directly, as class method:   1513229709
        Class variable, from parent: 1513229709
        Other class variable:        1513229709
class constant:   1513229709
as object method: 1513229709
as class method:  1513229709

It is best to simply use the parent's attribute or its method, which are both inherited by its subclasses. If you use an attribute then provide an accessor for it in each subclass that uses it, for users of those subclasses.

There are differences between these two and which to choose depends on design details. As a basic I'd go with the method. Remember that the parent's class variables are accessible to any user of its derived classes.

Once you get to working with objects in Perl I recommend getting familiar with its native system. Then at some point one should look into Moose and/or its light-weight sibling Moo

A note on class vs. object (instance) methods

A "class method" does not relate to any one object in particular, but rather to a class as a whole (say, return a number of objects created up to that point); thus it doesn't need an object's data. When a method is called on the class name it is the class name that is passed to it as the first argument and this is what makes it a "class" method.

An "object (instance) method" is meant to do work for a specific object, and when called on an object it is the object (reference to a Perl data structure used to store data in that class) that is passed to it.

Perhaps confusingly, we can call a method intended as a class method on an object as well, without errors (unless the method uses the class name which now it wouldn't have); this shouldn't be done as it muddies the purpose. Calling an instance-meant method with the class name would probably be bad, since it most likely does expect an object to be passed, while it will get a plain string with a class name.

This apparent ambiguity may be more confusing in this simple demo, as get_NOW can be called equally well on a class and on an object. Its intent is clearly of a class method. But it can also be useful with an object, in inherited classes where methods don't have the parent's name.

A note on packages and classes

A package

Declares the BLOCK or the rest of the compilation unit as being in the given namespace.

Then you define subs in it and provide a way for those symbols (names) to be exported to the caller so that they can use them conveniently. You have a library of functions and that is that. This is mostly called a "module," which is how I used the term above. See more in perlmod.

A class is code/namespace (for the lack of a better noun for it) that can be instantiated, meaning that you can create an instance of it, an object. This object "knows" what class it is associated with so that it can use subs provided in that class, making them methods. The system provides a way for a class to set up data (attributes), and each object gets its own copy. See perlootut.

In Perl, a class is defined as (is) a package. What makes it a class, so that one can instantiate an object for it, is that a reference is bless-ed into it. Normally there is a subroutine in that package in which a reference (typically to a hash) is bless-ed "into" the package (bless REF, PACKAGENAME) and returned. The perlobj says (my emphasis)

Objects are merely Perl data structures (hashes, arrays, scalars, filehandles, etc.) that have been explicitly associated with a particular class.

That object (the bless-ed and returned hashref) has an OBJECT flag in it (and a STASH), on top of other properties that a hashref internally has. So when it is dereferenced the interpreter can tell that the sub should be sought in that package; thus this is an object of a class.

Thus the modules in this question are just not classes (in the usual sense). Then what about "inheritance," a typical object-oriented term? Since Perl's OO system is minimalist some facilities apply to both generic modules and classes, and the terminology may get fuzzy here or there. The word "inheritance" is one such example as it may be used with its more generic meaning, where one unit picks up things setup in another.

I'd still advise to not use parent pragma on modules which aren't classes, even as it works.


A couple of semantic points, one brought up in a comment

  • Strictly speaking, a "class" is a package -- period. No need to have a bless-ing subroutine in it. An "object" is an instance of a package, by the virtue of the package being bless-ed into it. In a way, in Perl there are no special "class" and "object" things (even though those words are used) -- there are packages and their instances (associated with packages via bless.)

    All of this is clearly and precisely stated and discussed in the text but given a comment about it I'd like to clarify it further.

    Taking the above dead-serious-formally pushes the Perl's intentionally minimalist OO-model too far, and further than intended, I believe. What is a purpose of calling a package a class if it doesn't produce an object (has no bless-ing sub)? And am I going to pick any old reference in my code and bless a package with it, as I can? Um, no. (If anyone would say 'yes' please provide an example from the wild, of a non-trivial example of a package without bless that meaningfully acts as a class.)

    A "class" (package) is normally written so to work for a particular reference involved with bless-ing it, which has particular attributes, etc. Thus that reference and its bless-ing generally belongs to that package, and practically this is what makes that package a class.

    Well-- I note here that one can use bless in far more flexible ways. For example, say bless $a_reference; right in the (main::) program and then that $a_reference is associated with the package main::, thus making that package its "class." Or see a rather involved example in this Effective Perler article.

  • The referent is bless-ed, really. See, for instance the good old Bless My Referents

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Both you and @amon have valid answers that contributed to solving my problem. I was missing the `import` function from my `use Exporter` call, but I also needed to `use Internal qw(:all);` before `use parent qw(Internal);` in order to get rid of the error. The thing that tripped me up from the parent.pm documentation was this line: "Allows you to both load one or more modules, *while setting up inheritance* from those modules at the same time." (my emphasis) Not sure which answer to "accept" now... lol – Speeddymon Dec 13 '17 at 23:48
  • @Speeddymon Thank you for feedback! As for accept-choice ... your problem :)) I added a full example with classes. Please note that it is longish only because it offers a few ways and has lots of prints. Of course, there is _nothing_ wrong with using plain modules. – zdim Dec 14 '17 at 05:59
  • You don't need bless to make something a class. You need bless for something to make instances, but classes aren't required to make instances. – brian d foy Feb 19 '22 at 15:45
  • Well, yes, you'd better `bless` in a package if you want to call it a class. It is not formally required, and there are very rare (fringe I'd say) exceptions, but doing otherwise doesn't make sense. See text, and a footnote in particular. – zdim Feb 22 '22 at 00:28
0

You can't just use Exporter and expect it to work. In your Internal package you need to either inherit from it using

use Exporter;
our @ISA = 'Exporter';

or

use parent 'Exporter';

or, with more recent versions of the module, you can simply import its import subroutine

use Exporter 'import';

You could write your own import if you need something special, but in most typical cases there is no need

Borodin
  • 126,100
  • 9
  • 70
  • 144