Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

[Moose] extends(...) throws "Subroutine redefined" warnings

by muba (Priest)
on Oct 07, 2015 at 12:16 UTC ( [id://1144049]=perlquestion: print w/replies, xml ) Need Help??

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

I have a superclass (MyClass), which creates instances of a subclass on demand. MyClass implements a custom method "get_subclass_instance" for this. The subclass (SubClass) extends 'MyClass';, however, this generates a "Subroutine get_subclass_instance redefined" warning.

Minimal example:

# MyClass.pm package MyClass; use SubClass; use Moose; sub get_subclass_object { return SubClass->new; } 1;
# SubClass.pm package SubClass; use Moose; extends 'MyClass'; 1;
$ perl -MMyClass -e 0 Subroutine get_subclass_object redefined at MyClass.pm line 5. $

I understand that extends(...) tries to load the class(es) passed to it. Consequently, if I remove the Extends 'MyClass' line from SubClass.pm, the warning disappears.
But I don't want to remove that line, because SubClass does extend MyClass!

Of course, I could simply ignore the warning, but that doesn't feel clean to me. So what am I doing wrong, and how can I have MyClass load and instantiate SubClass, while SubClass explicitly extends MyClass?

Replies are listed 'Best First'.
Re: [Moose] extends(...) throws "Subroutine redefined" warnings
by MidLifeXis (Monsignor) on Oct 07, 2015 at 12:25 UTC

    I don't think that you would use SubClass within MyClass, but I could be wrong.

    --MidLifeXis

      The thought has passed my mind briefly, I think, but I rejected it because I do want to be able to just use MyClass; and automatically have SubClass available.

      However, it turns out you're right: if I move use SubClass out of MyClass.pm it just works fine. So I've come up with this workaround:

      • Have one wrapper package which loads all classes and subclasses
        (so that the script useing MyClass only needs one use statement to have it all)
      • Make it so that the wrapper package inherits all methods from the base class
        (so that all methods on the base class work when called on the wrapper package)
      • Have the base class as before, but without it useing its subclass, and now named 'BaseClass'
      • Have the subclass as before, but with extends 'BaseClass' instead.

      # MyClass.pm package MyClass; use BaseClass; # load the base class use SubClass; # load the sub class (which now extends BaseClass) our @ISA = qw(BaseClass); # have all BaseClass methods work on MyClass 1;
      # BaseClass.pm package BaseClass; use Moose; sub get_subclass_object { return SubClass->new; } 1;
      # SubClass.pm package SubClass; use Moose; extends 'BaseClass'; 1;

        It strikes me that your original scenario is along these lines (in non-Moose perl):

        # PkgA.pm use PkgB; ... 1;
        # PkgB.pm use PkgA; ... 1;

        It is a dependency loop.

        --MidLifeXis

Re: [Moose] extends(...) throws "Subroutine redefined" warnings
by Athanasius (Archbishop) on Oct 07, 2015 at 13:01 UTC

    Hello muba,

    Of course, I could simply ignore the warning, but that doesn't feel clean to me.

    For the record, I don’t get a warning, I get a full-blown compiler error:

    22:58 >perl -MMyClass -e 0 MyClass already has a metaclass, but it does not inherit Moose::Meta:: +Class (Class::MOP::Class=HASH(0x32b5d20)). at C:\Perl\Strawberry\stra +wberry-perl-5.22.0.1-64bit-PDL\perl\site\lib\Moose\Exporter.pm line 4 +84 Moose::import('Moose') called at MyClass.pm line 4 MyClass::BEGIN at MyClass.pm line 4 eval {...} at MyClass.pm line 4 require MyClass.pm at -e line 0 main::BEGIN at MyClass.pm line 4 eval {...} at MyClass.pm line 4 BEGIN failed--compilation aborted at MyClass.pm line 4. Compilation failed in require. BEGIN failed--compilation aborted. 22:58 >p5u v Moose Moose C:\Perl\Strawberry\strawberry-perl-5.22.0.1-64bit-PDL\perl\sit +e\lib\Moose.pm: 2.1603

    Are you using an earlier version of Moose?

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Uh... yep. -_-

      $ perl -v This is perl 5, version 18, subversion 2 (v5.18.2) built for MSWin32-x +64-multi-t hread Copyright 1987-2013, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. $ perl -MMoose -e "print Moose->VERSION" 2.1005
Re: [Moose] extends(...) throws "Subroutine redefined" warnings
by kcott (Archbishop) on Oct 08, 2015 at 04:58 UTC

    G'day muba,

    While I appreciate you've only shown a minimal example, and there could be a lot more going on than I'm aware of, my gut feeling is that you have a design flaw here. I say that because parent classes shouldn't know, or care, about their subclasses.

    If I change your test one-liner to something a little more substantial, I get much the same as ++Athanasius did in his response:

    $ perl -MMyClass -le 'print SubClass::->new()->get_subclass_object()' MyClass already has a metaclass, but it does not inherit Moose::Meta:: +Class (Class::MOP::Class=HASH(0x7f918c72e440)). at /Users/ken/perl5/p +erlbrew/perls/perl-5.22.0t/lib/site_perl/5.22.0/darwin-thread-multi-2 +level/Moose/Exporter.pm line 484 Moose::import('Moose') called at MyClass.pm line 3 MyClass::BEGIN at MyClass.pm line 3 eval {...} at MyClass.pm line 3 require MyClass.pm at -e line 0 main::BEGIN at MyClass.pm line 3 eval {...} at MyClass.pm line 3 BEGIN failed--compilation aborted at MyClass.pm line 3. Compilation failed in require. BEGIN failed--compilation aborted.

    By swapping the order in which the two modules are loaded in MyClass.pm, i.e.

    package MyClass; use Moose; use SubClass;

    your problem disappears:

    $ perl -MMyClass -le 'print SubClass::->new()->get_subclass_object()' SubClass=HASH(0x7fc4f2bb4148)

    However, I make absolutely no guarantees that this will not cause other problems with your complete code (which I haven't seen).

    If I remove the use SubClass; from MyClass.pm, and load SubClass.pm, instead of MyClass.pm, in the test:

    $ perl -MSubClass -le 'print SubClass::->new()->get_subclass_object()' SubClass=HASH(0x7fb6698259a8)

    the compile-time error is eradicated.

    I have a superclass (MyClass), which creates instances of a subclass on demand."

    Change &get_subclass_object to be more generalised. Something like:

    sub get_subclass_object { my $class = ref $_[0]; return $class->new; }

    Create another subclass (OtherSubClass.pm) for testing:

    package OtherSubClass; use Moose; extends 'MyClass'; 1;

    Now you can instantiate whatever subclasses you want:

    $ perl -MSubClass -le 'print SubClass::->new()->get_subclass_object()' SubClass=HASH(0x7fae2280f258)
    $ perl -MOtherSubClass -le 'print OtherSubClass::->new()->get_subclass +_object()' OtherSubClass=HASH(0x7fa721030da8)

    You can create as many subclasses as you want. MyClass knows nothing about them. All subclasses use the same method: &MyClass::get_subclass_object.

    The design flaw is now eradicated.

    The SYNOPSIS of the base Moose documentation has an example showing the use of extends. Read on for more information on extends in that documentation (which has links to further details).

    — Ken

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1144049]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2024-03-28 11:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found