Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

use base 'XYZ' and exporting

by japhy (Canon)
on May 28, 2005 at 12:59 UTC ( [id://461340]=perlmeditation: print w/replies, xml ) Need Help??

use base 'SomeModule' is cool for loading a module and putting it in your current package's ISA hierarchy, but it does not call SomeModule's import() method. Now, this might seem like an odd situation -- an OO module with a meaningful import() method -- but it's a situation I'm in, where an OO class defines several constants that I'd like to be exported to the calling package.
package LikeOOModule; use base 'MyOOModule'; sub foo { my $self = shift; if ($self->mode == OO_THIS) { ... } elsif ($self->mode == OO_THAT) { ... } }
To that end, I came up with a bit of code to include in MyOOModule that checks to see if 'base.pm' is anywhere in its calling stack, and if so, exports its symbols to the proper package (the one before 'base'). I'll show it, but not before admitting how pretty silly this all seems now. Should I have just sucked it up and done
use base 'MyOOModule'; MyOOModule->import;
? I mean, it's 100% less work, but I'd prefer the mechanism to be hidden from the person using MyOOModule.
# this handles 'use base "MyOOModule"' # which doesn't call 'import' { my ($level, $prev, $pkg); while (my ($curr) = caller $level++) { $pkg = $curr, last if $prev and $prev eq "base" # NB: base.pm shows and $curr ne "base"; # up twice in a row $prev = $curr; } __PACKAGE__->export_to_level($level, $pkg, @EXPORT) if $pkg; }

Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

Replies are listed 'Best First'.
Re: use base 'XYZ' and exporting (broken)
by tye (Sage) on May 28, 2005 at 15:15 UTC

    This is trivial to break:

    require MyOOModule; use base 'MyOOModule';

    Or just use the module in more than one place. Only the first place will have the potential to do this auto-import.

    It'd make more sense to change your import() to be able to set the caller's inheritance:

    use MyOOModule qw( -ISA ... );

    Or however you want to name the option.

    - tye        

      Hrm, that's an interesting idea. That second idea will work for my purposes.

      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: use base 'XYZ' and exporting
by borisz (Canon) on May 28, 2005 at 13:43 UTC
    I think it is wrong to change the meaning of use base ... for your module. If the behavior of use base is not what you expect, put a note into the docs of your module. That does not confuse users that expect, that use base does not influence the callers namespace. The docs for use base say
    Roughly similar in effect to package Baz; BEGIN { require Foo; require Bar; push @ISA, qw(Foo Bar); }
    so no import! Then I expect no import.
    Boris
Re: use base 'XYZ' and exporting
by dragonchild (Archbishop) on May 28, 2005 at 20:29 UTC
    What's wrong with
    package LikeOOModule; use base 'MyOOModule'; sub foo { my $self = shift; if ($self->mode == $self->OO_THIS) { ... } elsif ($self->mode == $self->OO_THAT) { ... } }

    Solves the problem and allows for overloading, if necessary. Don't let the fact that it's currently a constant blind you to the fact that you may want to override it later. (Particularly in testing, I've found.)


    • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
    • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
      But that's why I'm using constants, because their values shouldn't change. The concept of constant folding disappears when you do what you showed.

      Still, I agree with other people that this is an easily broken system, and goes against the definition of base. So I think I'll suck it up and write MyOOClass->import and be done with it all.


      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: use base 'XYZ' and exporting
by fergal (Chaplain) on May 28, 2005 at 15:28 UTC

    Won't this fail if a second module tries to use base?

    You could make your own customised class which follows the conventions of "use":

    use baseimp 'Foo' => ['Import', 'some', 'stuff'], 'Bar', # import defaults 'Buz' => [] # don't call import, just like use Buz () package baseimp; use base 'base'; # :) # unfortunately most modules (including base.pm) are hardwired to use +caller(0) so trickery is needed use Sub::Uplevel; sub import { my $pkg = shift; $base_import = UNIVERSAL::can(__PACKAGE__, "SUPER::import"); while (my $class = shift) { # let base do it's thing uplevel(1, $base_import, $class); # now do what use would do my $imp_args; if (ref($_[0])) { $imp_args = shift; undef $imp_args unless @$imp_args; } else { $imp_args = []; } if ($imp_args) { $class_import = UNIVERSAL::can($class, "import"); uplevel(1, $class_import, $class, @$imp_args); } } }

    So without any array refs, this works exactly like base except it _does_ import stuff, when you add array refs you get to control the imports exactly as with a normal use.

      Your &uplevel call don't work if base is compiled before you compile Sub::Uplevel, which it is, and you can't ever guarentee to load Sub::Uplevel before base from within your module, no matter how hard you try, which is annoying. Perl needs a real uplevel().

      Anyhow, while this is a fun module, I fail to see what good it really does. I'd just stick with

      use base 'Foo'; use Foo qw/ Import some stuff /; use base 'Bar'; use Bar; use base 'Buz'; use Buz ();
      It's a few extra chars, but I and everyone else will know exactly what happens. I guess this is one of those "just because you can doesn't mean you should" situations Perl constantly provides us with. :-)

      ihb

      See perltoc if you don't know which perldoc to read!

        OK, so forget uplevel and just reimplement what base actually does. It's a pity base.pm didn't do it right from the start. If you're going to act like "use" then you should do it properly.

        As for a just a few characters, I really have a problem with that. I don't mind a few characters extra for some things but this is perfect case of when I do mind because the few characters are a repetition of information (not just, say, some extra syntax).

        That means that if I need to change the base class, I need to remember to change both occurrences. This breaks the rule of having 1 source for every piece of information and leads to subtle errors. In this case it's fairly easy to keep them in sync as they're beside each other but in another way that makes it even worse because it would be so easy to fix.

Re: use base 'XYZ' and exporting
by tlm (Prior) on May 28, 2005 at 13:46 UTC

    Cool. My only nit with it (and it's a nano-nit) is that $pkg is being passed as an arg to export_to_level, which gives the mistaken impression that it means something, when in fact that argument is ignored. Do you happen to know of plans to make that argument actually meaningful in the not-too-distant future? If not, I'd make that line

    __PACKAGE__->export_to_level($level, undef, @EXPORT) if $pkg;

    the lowliest monk

Re: use base 'XYZ' and exporting
by ihb (Deacon) on May 29, 2005 at 18:46 UTC

    If you want to inherit, use base. If you want to import, call import. (If you want to import constants and benefit from constant folding, use use to import.) Those are the simple ideas.

    use MyOOModule qw/ THE CONSTANTS /; use base 'MyOOModule';
    If you make it so that base exports, you can't control what to import. Then you have to rely on @EXPORT, and can't use tags nor refuse to import.

    Personally I almost always name what I import, so that when I or someone else return to the code, it'll be clear from where a function was imported.

    For similar reasons I'd like to advice against using import to set up inheritance unless you really have to, since that is what base is for, and it'll surprise people who read your code.

    ihb

    See perltoc if you don't know which perldoc to read!

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2024-04-22 13:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found