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

Good morning, and my apologies in advance for a rather long and meandering supplication.

I came across something puzzling yesterday evening; it may likely prove to be just an unremarkable example of the Fool Pattern, but anyway. I'm creating a module that fakes a particular environment, mainly by declaring big sets of global variables. It started a bit like this

package Totally::Fake; our @ISA = qw/Exporter/; our ($fake, @faker); @EXPORT = qw/$fake @faker/;

The dream was that I could easily check the perl scripts I wrote for this environment using 'perl -c -MTotally::Fake -Mstrict my_script.pl', and all the variables will be there and strict won't complain.

First thing I don't understand
This didn't work. my_script.pl would refer to $fake and strict would complain. So I tried making a little test script:

use Totally::Fake; use lib '/path/to/dev/version'; print $fake;

Second thing I don't understand:
This did work - but only because of the presence of the use lib declaration. I could add a non-existent path to @INC, so loading exactly the same version of Totally::Fake from /usr/lib/perl, and it would work, where it wouldn't without use lib. This seems really weird to me. Why should 'use lib' affect my exports?

Third thing I don't understand
So some poking and tinkering later, I happened to try 'use base qw/Exporter/' instead of 'our @ISA = qw/Exporter/' in my module ... and it finally worked how I'd like it to. Which is good, but bad. I had a look at the documentation for base. How does inheriting using base differ from declaring @ISA, so that one works here, and the other doesn't?

With thanks in advance
ViceRaid

Replies are listed 'Best First'.
Re: Side effects of use-ing base (vs our @ISA)
by herveus (Prior) on Sep 10, 2003 at 11:17 UTC
    Howdy!

    use base ... does more than simply add stuff to @ISA; it also uses the modules. If you add use Exporter; to your version that modifies @ISA, it should work as you expected.

    yours,
    Michael

      Thanks, that makes sense to me now. perldoc base:

      'use base qw[Foo Bar]' is roughly equivalent to: BEGIN { require Foo; require Bar; push @ISA, qw(Foo Bar); } ... If any of the base classes are not loaded yet, *base* silently "require"s them.

      It seems like quite a subtle bug to fall into - you can use @ISA directly to declare a subclass of Exporter and think you're getting an import method from the superclass, but actually because you haven't loaded Exporter, silently nothing will happen when you use the subpackage...

      cheers
      ViceRaid

        It seems like quite a subtle bug to fall into

        Yes it does. But what it says to me is: "Don't export. Use the OO scheme fully instead. Minimize use of globals and declare and use them in one file only."

        --Bob Niederman, http://bob-n.com

        All code given here is UNTESTED unless otherwise stated.

Re: Side effects of use-ing base (vs our @ISA)
by Abigail-II (Bishop) on Sep 10, 2003 at 11:13 UTC
    Are you sure you need the 'use lib' in the "second thing"? For me it works with just the 'use Totally::Fake', without the need of 'use lib'.

    Also note that "-M Foo" is *not* the same as "use Foo;". Perlrun says:

    -M[-]module -M[-]'module ...' -[mM][-]module=arg[,arg]... -mmodule executes "use" module "();" before executing your program. -Mmodule executes "use" module ";" before executing your program. You can use quotes to add extra code after the module name, e.g., '-Mmodule qw(foo bar)'. If the first character after the -M or -m is a dash ("-") then the 'use' is replaced with 'no'. A little builtin syntactic sugar means you can also say -mmodule=foo,bar or -Mmodule=foo,bar as a short- cut for '-Mmodule qw(foo bar)'. This avoids the need to use quotes when importing symbols. The actual code generated by -Mmodule=foo,bar is "use module split(/,/,q{foo,bar})". Note that the "=" form removes the distinction between -m and -M.

    The important part is:

    -mmodule executes "use" module "();"

    Note the trailing (), meaning that the modules import routine will not be called. So, Exporter won't export the variables.

    Abigail

      Thanks Abigail

      Are you sure you need the 'use lib' in the "second thing"? For me it works with just the 'use Totally::Fake', without the need of 'use lib'.

      I've double-checked it and stripped it down to it's barest bones:

      ### Totally/Fake.pm use strict; package Totally::Fake; our @ISA = qw(Exporter); our ($fake, $faker); our @EXPORT = qw($fake $faker); ### my_script.pl use strict; # uncomment this to make it work # use lib '/long/path/to/nowhere'; use Totally::Fake; print $fake;

      ... and I'm still getting the odd behaviour (perl 5.8.0).

      Also note that "-M Foo" is *not* the same as "use Foo;" ... -Mmodule executes "use" module ";" before executing your program. (perldoc perlrun)

      Doesn't this mean -MFoo == use Foo; but -mFoo != use Foo;? In that -M will call the import routine for the default set of symbols, but -m will not call it.

      Cheers
      ViceRaid

        Your Totally/Fake.pm doesn't have a "use Exporter;". How adding 'use lib' makes perl compile my_script.pl seems like a bug to me.

        Doesn't this mean -MFoo == use Foo; but -mFoo != use Foo;? In that -M will call the import routine for the default set of symbols, but -m will not call it.

        Yes it does. I completely misread.

        Abigail