http://www.perlmonks.org?node_id=1214021

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

"An import list", you say? Who would ever need to pass an import list to an object oriented module?

...except that import lists are sometimes used for other purposes. An example would be use strict 'vars';, where 'vars' is passed to  ->import() so that the import routine can alter a specific behavior (not actually import something).

Consider a Moose-based class inheriting from a non-Moose base class. This happens, and often can work out fine. But what if that non-Moose base class needs an import list to configure some feature?

package Foo; use Moose; extends 'MyBase', ['-myfeature']; # An imaginary construct that not su +rprisingly, doesn't work.

extends accepts multiple parent classes, so extends 'MyBase', -myfeature; would blow up. Is there a solution that doesn't involve this?

package Foo; use Moose; use MyBase -myfeature; our @ISA = qw(MyBase);

Dave

Replies are listed 'Best First'.
Re: Passing an import list to Moose extends
by LanX (Saint) on May 03, 2018 at 19:41 UTC
    I've never worked with Moose, so pardon me if I'm talking "sundial" now. ..

    I took a glimpse into the Moose docs

    Each superclass can be followed by a hash reference with options. Currently, only -version is recognized :

    extends 'My::Parent' => { -version => 0.01 }, 'My::OtherParent' => { -version => 0.03 };

    So there is already a better syntax, just lacking support for more options.

    Then I had a look into the source of extends , seems like it's implemented using superclasses from Moose::Meta::Class

    If I were you, I'd try to patch it and see if it works like intended (or build my own xxtends ;)

    Since Moose is (rightfully) so proud of its MOP* there might be a proper way to extend extends.°

    HTH :)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Wikisyntax for the Monastery

    update

    *) probably using  Class::MOP::Class

    °) it's not easy to avoid puns here

      °) it's not easy to avoid puns here

      Indeed. I found it difficult to choose my words in the question I asked for the same reason.

      I'm certain that I can come up with a solution, and it may follow one of the paths you've explored. But I just keep thinking I can't be the first person to discover this need, considering how well-developed the Moose ecosystem has become. But you're on the right track, I think. So thanks!


      Dave

        I'm not sure if that's such a trivial requirement.

        My next idea was to import manually and run extends afterwards because all the docs say:

        When you pass classes to this method, we will attempt to load them if they are not already loaded .

        But here it becomes really tricky ... import into which namespace exactly?

        You are effectively setting @ISA to a superclass which has to be created with special import features, right?

        What if another part of your runtime needs the same superclass with different features?

        Well, this gets really abstract without a concrete use case.*

        But I think you need to create intermediate classes for different import options between your subclass and superclass.

        Or probably you just need to change your concept to mixins?

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery

        *) please help us understand better what your goal is.

Re: Passing an import list to Moose extends
by choroba (Cardinal) on May 03, 2018 at 21:23 UTC
    I still struggle to find an imaginable use case. strict isn't an object class.

    Also, how would you specify the import arguments for a non-Moose class inherited via parent or base?

    Here's what I've played with:

    My/Parent.pm

    package My::Parent; use warnings; use strict; sub import { my $name = { short => 'frob', long => 'frobnicate' }->{ $_[1] }; no strict 'refs'; *{$name} = *_frobnicate; } sub new { bless $_[1], $_[0] } sub _frobnicate { $_[0]{a} + $_[0]{b} } __PACKAGE__

    Plus a test to show how it works:

    Inheriting via parent is possible, just note that the import method would be inherited, but you can override it and call the SUPER class's method:

    My/SimpleChild.pm

    package My::SimpleChild; use warnings; use strict; use parent 'My::Parent'; sub import { shift->SUPER::import('short'); } __PACKAGE__

    And it works:

    In fact, you can use the same trick in Moose. I just added MooseX::NonMoose that handles inheriting from non-Moose classes correctly, and defined the a and b as proper attributes. For it to work, I needed to specify FOREIGNBUILDARGS so I can call the constructor without a hash reference:

    My/MooseChild.pm

    package My::MooseChild; use Moose; use MooseX::NonMoose; extends 'My::Parent'; has a => (is => 'ro'); has b => (is => 'ro'); sub import { shift->SUPER::import('short') } sub FOREIGNBUILDARGS { my ($class, %args) = @_; \%args } no Moose; __PACKAGE__->meta->make_immutable

    And again, it works.

    I'm not sure I'd go that way, though. I remember some monks preferred delegation over inheritance, and we can use it here, too:

    My/DelegChild.pm

    package My::DelegChild; use Moose; use My::Parent 'short'; has a => (is => 'ro'); has b => (is => 'ro'); has _parent => (is => 'ro', lazy => 1, default => sub { my ($self) = @_; 'My::Parent'->new({ a => $self->a, b => $self->b }); }, handles => ['frob']); no Moose; __PACKAGE__->meta->make_immutable

    It can all get very fragile and weird, because the import method changes the global state (and imagine what would happen if the parent's import method used state $name instead of my). It might be impossible to have two classes, each using different import parameters of the base class.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Passing an import list to Moose extends
by Anonymous Monk on May 04, 2018 at 13:50 UTC

    What about

    package Foo; use Moose; extends 'MyBase'; use MyBase '-myFeature';

    The 'use' is idempotent, so provided Moose actually populates %ISA (and it seems to me it's a serious misfeature if it does not) it won't load MyBase again. But it WILL call import(). If you are nervous about that, the more-verbose

    BEGIN { MyBase->import( '-myFeature' ); }

    should have the same effect, PROVIDED it is called after MyBase has been loaded.

    I do not know whether the order of the extends() and use() is important. The use() does not care, but I can not speak for Moose.

      Was thinking the same. Either use 'use' or call the import function directly.

      Looking at this, why trying to call the import functions at all actually? You should always be able to inherit, Since use Foo '-myFeature' (your class is 'used') will eventually have the same effect.

      Wonder what will happens with the selected option in case the user of your class will specify a different option. What if you have already 'used' the class with a certain option. Will the import list specified by the user of your class still be used or silently ignored? The 'use' clause is executed only once right? Or will the import function still be executed?

      Calling the import function doesn't seem to have anything to do with extends. Just like use base/parent MyBase is not calling an import function either. Is this actually specific to Moose?

        > The use clause is executed only once right? Or will the import function still be executed?

        use is just require + import at compile time.

         BEGIN { require Module; Module->import( LIST ); }

        • The require will happen only once (it sets and checks %INC, there is no point in loading the code multiple times from disk)
        • the ->import() method will (must) be called each (compile) time.
        But I doubt the OP wants to use import for normal exports of functions.

        One is free to use import for any kind of compile time effects (like executing a source filter or activating pragmas or even calling extend() )

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery