Exporter & inheritance 
Author Message
 Exporter & inheritance

Dear pearlers,

Excuse my presumptiveness, but I find the behaviour of the Exporter to
be counter-intuative. It is probably because I expect it to have a
similar behaviour to import in Java or ada.

import package1.classname;
...
new classname() // shorthand for: new package1.classname()

In perl calling 'use XXX' can result in XXX exporting some symbols into
your package's symbol table. This has the effect of making the exported
entities visible without qualification within your package. All is well
and good. However, because they are put into the package symbol table,
they become part of that packages public interface. Of course, if you
never tell anybody about this then it usualy causes no harm.

However, below is a (somewhat contrived) case where this export
mechanism causes unexpected behaviour.
- Package Base defines a method print_array, which does some array
formatting.
- Package Derived inherits from Base, and expects to simply inherit that
method.
- However, Package Derived also uses Messup, as it contains countless
usefull functions for doing stuff.

The problem is that when Messup exports its print_array then this
becomes the function that will be invoked by Derived->print_array. I
realize that I can say 'use Messup()' to stop it being imported, but the
whole point of the Exporter is to make function and variable names easy
to type without prefixing them with a package. Also, I may not know
exactly what Mesup exports by default, so not realize that my method is
being clobbered.

It comes back to the issue that when symbols are exported, I expect them
to be visible for ease of use, not become melded into my packages
interface. For example, I can call package->carp for any package that
has imported Carp.pm, even though it realy makes no sence to call
Exporter->carp.

Any thoughts, workarounds etc.?

Matthew

#file Base.pm#
package Base;

sub print_array {
  my $self = shift;

Quote:
}

1;

#file Messup.pm#
package Messup;

sub print_array {

Quote:
}

1;

#file Derived.pm#
package Derived;

use Base;
use Messup; # nobles SUPER::print_array

1;

#driver.pl#
use Derived;

Derived->print_array(qw(fish dog cat));



Sat, 28 Jul 2001 03:00:00 GMT  
 Exporter & inheritance


: In perl calling 'use XXX' can result in XXX exporting some symbols into
: your package's symbol table. This has the effect of making the exported
: entities visible without qualification within your package. All is well
: and good. However, because they are put into the package symbol table,
: they become part of that packages public interface. Of course, if you
: never tell anybody about this then it usualy causes no harm.

[snip]

: Any thoughts, workarounds etc.?

Reasons such as those you described are among the motivation for this
excerpt from the perlmodlib manpage:

    Select what to export.
        Do NOT export method names!

        Do NOT export anything else by default without a good
        reason!

        Exports pollute the namespace of the module user.  If


        risk of name clashes.

        [...]

        As a general rule, if the module is trying to be
        object oriented then export nothing. If it's just a


A module should be very clear in its documentation about what symbols
it exports.  If the set of exported symbols changes, then the module's
documentation should scream this loudly (and early).  I certainly
wouldn't want to use such a stealthily promiscuous piece of code.  If

module's code.

Greg
--
Sam:  What'd you like, Normie?
Norm: A reason to live. Gimmie another beer.



Sat, 28 Jul 2001 03:00:00 GMT  
 Exporter & inheritance

Quote:

>: import package1.classname;
>: ...
>: new classname() // shorthand for: new package1.classname()

>        Perl has no such shorthand system for class names, nore can one be
>        created.

Sure it does.

  package Foo::Bar::Baz::Buz;


  package main;
  $x = new Foo;

You can also replace the ISA line with:

  *Foo:: = *Foo::Bar::Baz::Buz::;

But I'm not sure offhand how well that would work.

--

%PGPKey = ('B76E72AD', [1024, '0824090B CE73CA10  1FF77F13 8180B6B6'])



Sat, 28 Jul 2001 03:00:00 GMT  
 Exporter & inheritance
[posted & mailed]

        >snip<
: Excuse my presumptiveness, but I find the behaviour of the Exporter to
: be counter-intuative. It is probably because I expect it to have a
: similar behaviour to import in Java or ada.

        It's closer to Ada then Java, but still not the same. -BTW, trying
        to apply nearly Java designs to Perl or vis-versa is nearly always
        a huge mistake as there object models are lightyears apart.

        Assuming you can simply pick up one language's object model and
        systems to be used in another is a common and fatal mistake.

: import package1.classname;
: ...
: new classname() // shorthand for: new package1.classname()

        Perl has no such shorthand system for class names, nore can one be
        created.

        Exporter is not for OOP or classes.  It's for exporting symbols;
        functions (NOT methods!), constants, globals, etc.  It's much closer
        to a mix of #include of cpp and some Ada pieces.  Think "use" not
        "import".

: In perl calling 'use XXX' can result in XXX exporting some symbols into
: your package's symbol table. This has the effect of making the exported
: entities visible without qualification within your package. All is well
: and good. However, because they are put into the package symbol table,
: they become part of that packages public interface. Of course, if you
: never tell anybody about this then it usualy causes no harm.

        Exactly.  So don't do that.

: However, below is a (somewhat contrived) case where this export
: mechanism causes unexpected behaviour.
: - Package Base defines a method print_array, which does some array
: formatting.
: - Package Derived inherits from Base, and expects to simply inherit that
: method.
: - However, Package Derived also uses Messup, as it contains countless
: usefull functions for doing stuff.

        Ding, ding, ding!

        You're using OOP here.  You don't likely have a reason to be

: The problem is that when Messup exports its print_array then this becomes
: the function that will be invoked by Derived->print_array.
      ^^^^^^^^                         ^^^^^^^^^^^^^^^^^^^^
        First, "function" != "method".  The sooner you understand the suttle
        but important difference between the two the happier your Perl
        coding will be.

        print_array() is a method call.  You should simply not be exporting
        it at all.

: I realize that I can say 'use Messup()' to stop it being imported, but the
: whole point of the Exporter is to make function and variable names easy to
: type without prefixing them with a package.

        Exactly.  The problem is that you're not using functions, you're
        using methods.  Don't export methods.

: Also, I may not know exactly what Mesup exports by default, so not realize
: that my method is being clobbered.

        `perldoc Mesup'

: It comes back to the issue that when symbols are exported, I expect them
: to be visible for ease of use, not become melded into my packages
: interface. For example, I can call package->carp for any package that
: has imported Carp.pm, even though it realy makes no sence to call
: Exporter->carp.
:
: Any thoughts, workarounds etc.?

        You're calling functions as methods again, don't do that.  At
        best you'd call Carp::carp(), but likely never Carp->carp().

        Also, don't use inheritance for functions, only methods.

        Buy "Advanced Perl Programming" ISBN#1565922204 (www.ora.com).  Read
        up on chapters 6 through 8, at least.

        Perl will allow OO, procedural, functional, and nearly any other
        methodology to be used, all at the same time if you like.  This
        brings both power and responsibility.  If you are going to mix them,
        you need to be sure you're doing it for the right reasons as the
        language can't help you enforce against bad thoughts (as man OO
        languages try to do by simply removing any non-OO abilities
        completely).

        If you still want to import the function but have a conflict,
        resolve it manually:

                use Carp ();
                *myCarp = *Carp::carp;

        You can even localize this to the body of a function or method
        call:

                sub foo {
                    local *myCarp = *Carp::carp;
                }

        Although many (myself included) will see that as non-intuitive, so
        you might be better off with delegation:

                sub myCarp { goto &Carp::carp }

        BTW, this is a "magic" goto.  It's not the same (or as bad) as your
        normal C or BASIC goto.

--

BSD:  A psychoactive drug, popular in the 80s, probably developed at UC
Berkeley or thereabouts.  Similar in many ways to the prescription-only
medication called "System V", but infinitely more useful. (Or, at least,
more fun.)  The full chemical name is "Berkeley Standard Distribution".



Sun, 29 Jul 2001 03:00:00 GMT  
 Exporter & inheritance

+-----
| This seems beside the original poster's point.  I think what he was saying
| boils down to this: wouldn't it be nice to have entries in a package's symbol
| table that were visible inside the package, but not outside?  I know this
+--->8

        package Foo;
        use HTTP::Request::Common ();
        my $GET = \&HTTP::Request::Common::GET;

        my $request = &$GET 'http://www.perl.com';

Lexicals being what they are, I don't think there's a way to hide that in
a pragma.  Then again, this *is* Perl....

(Hmmm.  New syntax, if you really want this:  "my &foo = sub { ... }" ?)

--


carnegie mellon / electrical and computer engineering                    KF8NH
                          Kiss my bits, Billy-boy.



Sun, 29 Jul 2001 03:00:00 GMT  
 Exporter & inheritance


Quote:
>         package Foo;
>         use HTTP::Request::Common ();
>         my $GET = \&HTTP::Request::Common::GET;

>         my $request = &$GET ' http://www.*-*-*.com/ ';

how about having:

package Foo;
use Exporter(funcRefs=>1);
use HTTP::Request::Common;
my $request = $GET->(' http://www.*-*-*.com/ )

causing Exporter to set the global $Foo::GET to \&HTTP::Request::Common::GET? I
know that this still {*filter*}les on your space, but at least I can't say
Foo->GET(' http://www.*-*-*.com/ ') anymore by accident.

Quote:

> Lexicals being what they are, I don't think there's a way to hide that in
> a pragma.  Then again, this *is* Perl....

> (Hmmm.  New syntax, if you really want this:  "my &foo = sub { ... }" ?)

> --


> carnegie mellon / electrical and computer engineering                    KF8NH
>                           Kiss my bits, Billy-boy.



Tue, 31 Jul 2001 03:00:00 GMT  
 Exporter & inheritance

        >snip<
: >        Perl has no such shorthand system for class names, nore can one be
: >        created.
: Sure it does.
:
:   package Foo::Bar::Baz::Buz;


:
:   package main;
:   $x = new Foo;

        That's not shorthand, that's a subclass. -And besides, most would
        shorten to "Buz" not "Foo". :-)

: You can also replace the ISA line with:
:
:   *Foo:: = *Foo::Bar::Baz::Buz::;
:
: But I'm not sure offhand how well that would work.

        Probably not well in many cases.  I would think inheritance would
        freak, as may autoloading.

--

BSD:  A psychoactive drug, popular in the 80s, probably developed at UC
Berkeley or thereabouts.  Similar in many ways to the prescription-only
medication called "System V", but infinitely more useful. (Or, at least,
more fun.)  The full chemical name is "Berkeley Standard Distribution".



Tue, 31 Jul 2001 03:00:00 GMT  
 Exporter & inheritance

Quote:

> But there are times when I would want the
> following to choke:

[Package Foo uses package HTTP::Request::Common.  Code in package main,
which didn't explicitly invoke H::R::C, nevertheless then uses a
function from H::R::C.]

Quote:
> It would seem like an ultimately Perlish thing to be able to allow the
> convenience of the first GET without the abstraction barrier breach of the
> second.  Of course, it would also be a very Perlish thing for there already to
> be a Way To Do It that I just don't know, so let me know.

Kick this around for a few minutes:

        {       package Foo;

                local %main::;

                use Carp;

                sub check_positive($)
                {       my $thing = shift;
                        carp "Whoa! $thing" if $thing <= 0;
                }
        }

        package main;

        Foo::check_positive 2;
        Foo::check_positive -2;

        # Here's the good bit

        carp("That's all, folks");

&carp is available inside the big outer block, but not beyond.  If
package main wants to use &carp, it needs another 'use Carp' directive.
The code works by localising the symbol table for package main, which
rejoices in the name of '%main::'.  At the end of the outer block,
%main:: is restored to the condition it had just before it was
localised, which means you must localise %main:: after declaring package
Foo and exporting anything in Foo that needs exporting.

There's one big problem with this technique: what would have happened if
package Carp had done some important or expensive initialisation and
termination?  That code would have been run twice.  This may or may not
be important.

If localising %main:: is too barbaric for your taste, you can get
package-local functions like this:

        {       package Foo;

                sub new

                        bless \$val, $pkg;
                }

                my $double = sub
                {       my $self = shift;
                        $$self *= 2;
                };

                sub quadruple
                {       my $self = shift;
                        $double->($self);
                        $double->($self);
                }

                sub value
                {       my $self = shift;
                        $$self;
                }
        }

        package main;

        my $thing = new Foo 22;
        quadruple $thing;
        print '$thing is now ', $thing->value, "\n";
        double $thing;
        print '$thing is now ', $thing->value, "\n";

When you want to keep a function or method local to package Foo, you
don't declare it in the usual way: instead, you create an anonymous
function and store a reference to it in a lexical variable.  All
function calls must then be indirect ($double->($self)) rather than
direct (double $self), which is ugly and carries a performance penalty.
But you may decide that the enforced privacy is necessary and worth the
hit.

Markus



Tue, 31 Jul 2001 03:00:00 GMT  
 
 [ 9 post ] 

 Relevant Pages 

1. Module design question (use Exporter vs require Exporter)

2. Module design question (use Exporter vs require Exporter)

3. Attribute inheritance, accessor methods & namespaces

4. Circular use defeats Exporter?

5. local() breaks with imported vars (Exporter bug?)

6. Exporter enhancement to allow import('-options')

7. Undesirable Exporter.pm behavior

8. first time Exporter troubles

9. Why can't I use Exporter?

10. Undocumented bahaviour of Exporter (%EXPORT hash)

11. new Exporter functionality

12. Where and what is Exporter.pm

 

 
Powered by phpBB® Forum Software