Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Exporter/@ISA confusion

by qhen (Acolyte)
on Jun 09, 2014 at 06:20 UTC ( [id://1089226]=perlquestion: print w/replies, xml ) Need Help??

qhen has asked for the wisdom of the Perl Monks concerning the following question:

Hello monks,

Hopefully someone can alleviate my confusion w.r.t. when to use Exporter/@ISA when subclassing.

My code - which works as expected:
package MyClass::A; use strict; use warnings; use Exporter; use base qw(Net::Server::PreFork); #our @ISA = qw(Exporter); our @EXPORT = qw(new serve); our @EXPORT_OK = qw(); sub new { my ($class_name) = @_; my $self = {}; bless $self, $class_name; #... return $self; } sub process_request { #... } sub serve { my ($self) = @_; MyClass::A->run(); return $self; } my $s = MyClass::A->new(); $s->serve();
However, if I uncomment #our @ISA = qw(Exporter); Then I get the error
Can't locate object method "run" via package "MyClass::A" at b.pl line + 28.

I normally create all my classes using our @ISA = qw(Exporter);, so I'm not clear on why this is failing now. Is it because I'm sub-classing Net::Server::PreFork?

Second - and I suppose related - question is that if I comment out as follows:

#use Exporter; use base qw(Net::Server::PreFork); #our @ISA = qw(Exporter);

everything still works normally, so why on earth should I bother with Exporter anyway?

Thanks

Replies are listed 'Best First'.
Re: Exporter/@ISA confusion
by Corion (Patriarch) on Jun 09, 2014 at 06:42 UTC

    The contents of @ISA are what determines inheritance in Perl. use base sets @ISA and your assignment of @ISA= 'Exporter' overwrites that.

    Are you sure that you really want to do both, export from your package and have your package a class? Usually, a class doesn't need to export anything, especially not its constructors.

Re: Exporter/@ISA confusion
by Bloodnok (Vicar) on Jun 09, 2014 at 07:26 UTC
    As Corion has already said, the use of Exporter is frowned upon when writing classes, but if you really have to use it in such a way, then try rewriting our @ISA = qw(Exporter); as push @ISA, qw(Exporter); - in this way, rather than being overwritten, @ISA is extended.

    A user level that continues to overstate my experience :-))
Re: Exporter/@ISA confusion
by 2teez (Vicar) on Jun 09, 2014 at 07:49 UTC

    Hi qhen,

    ...so why on earth should I bother with Exporter anyway?...

    Maybe you should take a look at Exporter to start with.

    Secondly, just like Corion said, I would re-frame, except on purpose ( and you really know what you want and what you are doing ), I will advice you don't export anything when doing OO in Perl.

    You probably want to take a look into modern ways of doing OOP in Perl using Moose and it's cousins.
    You might also want to look into perlootut and perlobj

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
      There is no reason to use Moose or any heavy handedness for this. The other side of the coin here is that in the script using your module, you may simply use the constructor by calling it in its fully qualified name space. Once you have your blessed reference, there is no need to export any of the instance methods. For example the script using your new module would work perfectly fine if you omitted anything having to to with @INC or Exporter in the .pm:
      use strict; use warnings; use MyClass::A (); my $s = MyClass::A->new(); $s->serve();
      The Perl module simply has do look like the following:
      use strict; use warnings package MyClass::A; sub new { my $pkg = shift; my $self = {}; return bless $self, $pkg; } sub serve { my $self = shift; # .. do whatevs } 1;
      In the past Exporter was used to make it more convenient to load subroutines for use without having to deal with namespaces, but as it turns out using fully qualified namespaces is a really great thing to do. If you create a simple object via module using "traditional" Perl OOP, then you only have to type out the namespace once during construction. After that, the fact that the instance variable is blessed with the namespace means that you don't have to keep telling Perl what to use.

        Thanks - that's what I needed to know. I've been trying to stick to the "modern" Perl way (ie, following recommendations by chromatic, conway, et al), and also trying to keep things simple and clean without having to resort to Moose, etc.

        I've been using Exporter/@ISA and always wondered whether it was really needed as of Perl 5.8 (admittedly I have not spent the time to study the matter; time I often don't have) - the more syntactic cruft I can cut out, the better. I sometimes look at the relative elegance of Python and wish the same were true for Perl (which I love).

        So, bottom line, I can simply exclude use Exporter; our @ISA = qw(Exporter); when creating a new class and/or inheriting from an existing, but still use:

        our @EXPORT = qw(new serve); our @EXPORT_OK = qw(foo);
        to allow optional/selective exporting of methods.

        UPDATE:

        Someone else commented:

        You need to use use Exporter; in your module to get the @EXPORT array into the calling script/module...

        So, in order to use @EXPORT/@EXPORT_OK, I still need to use Exporter.

Re: Exporter/@ISA confusion
by boftx (Deacon) on Jun 09, 2014 at 07:52 UTC

    General rule of thumb: If you have @ISA or use base (with use parent being preferred to both of those nowadays) then you should not be using Exporter at all.

    Beyond that, exporting sub-routines or variables from a library module (as opposed to an OO package) seems to be considered bad form today (though there could be legitimate reasons to do so in a $work environment.)

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.

      General rule of thumb: If you have @ISA or use base (with use parent being preferred to both of those nowadays) then you should not be using Exporter at all.

      ok, but what if I want to allow the user of my New::Class to selectively import methods? in that case I need to use @EXPORT/@EXPORT_OK in order to do so.

      Beyond that, exporting sub-routines or variables from a library module (as opposed to an OO package) seems to be considered bad form today (though there could be legitimate reasons to do so in a $work environment.)

      I don't know what that means. To me, My::Package is synonymous with an OO class... unless I'm being dense and not understanding you correctly.

        You are correct that Exporter is used to allow users to selectively import functions from a module. That said, you don't need to do that with an OO module. The object itself effectively does that when it is called on a method. There is no need to export (or make available for export) an OO method.

        There is no need to export a function, though. Any sub-routine in a module can be called directly by means of using the package name, i.e. My::Package::some_function{}. This form of usage is becoming more popular that taking a chance on namespace collisions by exporting some_function().

        It is the same with OO packages. A blessed object can call any method defined in the package without it being exported first: $obj->some_function() is effectively making a call to My::Package::some_function( $obj ) (but a blessed object has a few more properties, of course.)

        There are things that can be done to essentially hide sub-routines from being available that way, but that is a different topic. :)

        You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Exporter/@ISA confusion
by vinoth.ree (Monsignor) on Jun 09, 2014 at 09:56 UTC
    @ISA:

    Each package contains a special array called @ISA. The @ISA array contains a list of that class's parent classes, if any.

    Exporter:

    You need to use use Exporter; in your module to get the @EXPORT array into the calling script/module, when anyone invokes a use declaration to load your module, it calls the import method from your module to fetch any symbols it needs into the package of the invoker. Your module (the one doing the exporting) can define the import method any way it pleases, but the standard way is to inherit the method from the Exporter class module.

    The Exporter module serves as a base class for modules that wish to establish their own exports. Oddly, object-oriented modules typically don't use Exporter, since they don't normally export anything.

    The import() method is invoked as a class method in your module: MyModule->import(LIST). However, since you didn't define an import method in your module, you'll automatically make use of the Exporter::import method through inheritance.


    All is well

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1089226]
Approved by 2teez
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-20 00:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found