Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Need help with clashing method names

by dk (Chaplain)
on Feb 13, 2009 at 14:13 UTC ( [id://743606]=perlquestion: print w/replies, xml ) Need Help??

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

Hello monks,

I'll need help with the following problem. Consider we have a class A, that inherits from class C and uses some syntax sugar as defined in B. Here's A:

package A; use lib '.'; use strict; use warnings; use B qw(moo); use C; use Exporter; our @ISA = qw(C); A-> new; moo; 1;

Here's B:

package B; use strict; use warnings; use Exporter; use vars qw(@ISA @EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(moo); sub moo { print "B::moo\n" } 1;

and here's C:

package C; use strict; use warnings; sub new { bless({}, shift)-> moo }; sub moo { print "C::moo\n"; } 1;
all three in 3 different files, for example. Now, running that code, surprisingly for me, yields "B::moo" twice, whereas I'd expect that first time it would print "C::moo". As you can see, B::moo is being invoked instead of C::moo.

Now, the question. I understand how it works, but I don't know, if there are any good practices to protect A and C from being abused that way? It's a fairly realistic scenario if A doesn't know that C::moo exists at all, or even worse, doesn't exist today but will exist in a newer version of C. It's not uncommon to have third-party modules to export names, but is there a simple technique to import them in a safer manner?

Thank you!

Replies are listed 'Best First'.
Re: Need help with clashing method names
by ikegami (Patriarch) on Feb 13, 2009 at 14:49 UTC

    It's not uncommon to have third-party modules to export names,

    I always explicitly specify the functions I import for other reasons, so it doesn't matter what third-party modules export.

    use B qw( ); # Import nothing
    use B qw( foo bar ); # but not moo

    It doesn't solve the problem, but it limits the chance of it happening.

    use B qw( moo ); # doh!

    If you were paranoid, you could import as follows:

    use B qw( ); BEGIN { *_moo = \&B::moo; }

    Then the imported sub will be named _moo. But I don't think it's worth the effort.

      Indeed, explicit specification is a tiny bit better than implicit like tag-based ":all" etc, but it still doesn't solve problem if the imported name will be used in the next version of the parent class, for example.

      I've sent a patch to initiate a discussion in perl5-porters, we'll see how it goes.

Re: Need help with clashing method names
by Herkum (Parson) on Feb 13, 2009 at 14:47 UTC

    Trying using 'base' to inherit methods from another module and avoid automatically importing methods. Better to explicitly to use a complete package path than depend upon exporting. Example:

    A::moo(); # explicit and obvious moo(); # can be problematic and because you are unsure of exactly whic +h one you are calling.

    There are some packages that export functions, and you should be very selective about those modules. It can very painful to try and debug those situations when you have two modules with the same function name.

      Trying using 'base' to inherit methods from another module and avoid automatically importing methods.

      No one said B::moo was a method. For example, B::moo could be Encode::encode

      { package Child; use Base qw( ); use Encode qw( encode ); our @ISA = 'Base'; sub another_method { ... my $x = encode('UTF-16le', $y); ... } } { package Base; sub encode { ... } sub some_method { ... my $c = $self->encode($p); # Can call Encode::encode($self,$p)! ... } } Child->new()->some_method();
Re: Need help with clashing method names
by ELISHEVA (Prior) on Feb 13, 2009 at 16:00 UTC
    There is no "safe" import. Your best defense is to keep in the forefront of your mind that in Perl, a "method" is nothing more than a subroutine. Consequently, any subroutine that you import into a file, becomes part of that package's name space and hence a method of any object blessed into that package.

    More specifically, the statement use B qw(moo) adds "moo" to the A namespace, thus defining A::moo(...) - this means that class A now has its own definition moo that overrides anything it inherits from C. Thus when you called A->new the code with variables resolved looked something like this:

    my $self=bless({},"A"); $self->moo; #equivalent to $self->A::moo

    A::moo(...) just happens to be an alias for B::moo(...) so you get the print out "B::moo" rather than "C::moo".

    The lesson: if you don't want subroutine "foo" to magically become part of class "baz", don't import it into "baz"'s namespace. The best way to prevent this is to follow Almut's advice and use the incantation use B () whenever you import a class, unless you know that it is well behaved and doesn't auto-export anything.

    Of course, if you want "blah::foo" to be a method of Class "baz", you can call use blah qw(foo) without fear.

    Best, beth

Re: Need help with clashing method names
by almut (Canon) on Feb 13, 2009 at 14:45 UTC
    It's not uncommon to have third-party modules to export names, but is there a simple technique to import them in a safer manner?
    use B ();

    i.e., simply don't import stuff you don't want to have imported?  (though maybe I've misunderstood the issue here...)

      consider the code:
      package MyCGI; use base qw(CGI); use POSIX qw(access); MyCGI-> new();
      Today there's no such thing as CGI::access, so we're safe. Tomorrow, next version of CGI.pm might introduce internal "sub access" that is supposed to be called from new(), but in the code above, it mysteriously wouldn't.

        Yes sure, I see what you mean, but I think you're kinda mixing two separate issues here — so it wasn't immediately clear to me, with which you wanted help.

        For one, there's the question of how to avoid to inadvertendly import stuff into your namespace that might clash with something else (this is what I tried to answer).

        Then, there's the problem of accidentally introducing something in the inheritance hierarchy which does interfere with the so far established lookup mechanics in the code. For the latter problem it's irrelevant whether you import access from POSIX, or whether you yourself happen to write a new routine MyCGI::access. If it's found first along the inheritance chain for a certain type of object, it will be called - that's it. I don't think there's much you can do to prevent this from possibly happening within the standard Perl5 OO framework.

        OTOH, I have to say that I personally cannot remember to have ever encountered that type of problem in practice within the last decade of my programming in Perl5 (and I've used quite a number of modules during that time). But maybe I just don't recall the incidence, because it was trivial to diagnose and fix... Dunno.

Re: Need help with clashing method names
by phaylon (Curate) on Feb 15, 2009 at 16:36 UTC
    I use namespace-clean (Disclaimer: I implemented it):

    package Foo; use strict; use warnings; use Scalar::Util qw( blessed ); use parent 'Something::With::A::blessed::Method'; use namespace::clean; # this will show the method in the parent class say Foo->can('blessed');

    Ordinary morality is for ordinary people. -- Aleister Crowley
      Oh this is great!!! Almost what I've needed. I've noticed you employed end_of_scope for this purpose, but I wonder if dereferencing the GV and checking GvIMPORTED() flags would be easier?

      Thank you!!

        Honestly? I have no idea what you're talking about :) It was only partly my idea, most of the kudos should go to Matt Trout and Florian Ragwitz.


        Ordinary morality is for ordinary people. -- Aleister Crowley
Re: Need help with clashing method names
by Anonymous Monk on Feb 13, 2009 at 22:46 UTC
    D:\>perl -e"use B qw(moo);" "moo" is not exported by the B module Can't continue after import errors at -e line 1 BEGIN failed--compilation aborted at -e line 1.
    B doesn't export moo :D

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (3)
As of 2024-04-23 23:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found