http://qs321.pair.com?node_id=598820

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

Could somebody please explain why the following does this?
#! /usr/bin/perl use warnings; use strict; use test(); my $output = testroutine(); print $output, "\n";

and this has the same result...
#! /usr/bin/perl use warnings; use strict; use test; my $output = testroutine(); print $output, "\n";
the result being...
Undefined subroutine &main::testroutine called at ./test.pl line 8.
If I run the following:
$ perl -e 'map {print $_,"\n"} @INC' /usr/lib/perl5/5.8/cygwin /usr/lib/perl5/5.8 /usr/lib/perl5/site_perl/5.8/cygwin /usr/lib/perl5/site_perl/5.8 /usr/lib/perl5/site_perl/5.8/cygwin /usr/lib/perl5/site_perl/5.8 /usr/lib/perl5/vendor_perl/5.8/cygwin /usr/lib/perl5/vendor_perl/5.8 /usr/lib/perl5/vendor_perl/5.8/cygwin /usr/lib/perl5/vendor_perl/5.8 .
Now if I alter test.pl to look like:
#! /usr/bin/perl use warnings; use strict; use lib '.'; #My Change use test(); my $output = testroutine(); print $output, "\n";
When run, the program outputs the following:
This is a test
For information test.pm which is in the same directory as test.pm has the following code:
sub testroutine { return "This is a test"; } 1;
Now I may be very ignorant of the use. However, I would expect my first example to work if my second example works. Why would enabling use lib import the module into my namespace? When before I use use lib, '.' is in my @INC. Second, I would think that in actuality both examples should fail since I'm calling test.pm by use test(), I would think this would NOT populate anything into my namespace? I hope this long post makes sense, for I am eager to learn.

Thanks!
s;;5776?12321=10609$d=9409:12100$xx;;s;(\d*);push @_,$1;eg;map{print chr(sqrt($_))."\n"} @_;

Replies are listed 'Best First'.
Re: Use, Exporter, I'm Dizzy.
by ikegami (Patriarch) on Feb 07, 2007 at 17:20 UTC

    You have three major problems.

    • Perl comes with a module called test. You aren't loading the module you think you are loading until you added use lib '.';, which causes your module to be found before the one that comes with Perl.

    • use test (); means import nothing

      use test; means import everything the module exports by default. With module using Exporter, that would be the items listed in @EXPORT. In the case of your module, that means import nothing.

    • Modules loaded using require (including those loaded using use) must have a package statement for them to work correctly when loaded from two different namespaces (such as from the main script and from a module).

      You'll notice by adding the package statement that your module will need to use Exporter (or similar) to export testroutine to the caller.

    #!/usr/bin/perl use warnings; use strict; #use MyTest; # Imports what's in the module's @EXPORT #use MyTest ( ); # Won't work. #use MyTest qw( ); # Another way of writing the same thing. use MyTest qw( testroutine ); # Imports testroutine and nothing else. my $output = testroutine(); print $output, "\n";
    # MyTest.pm use strict; use warnings; package MyTest; BEGIN { our @EXPORT = qw( ); # Export nothing by default. our @EXPORT_OK = qw( testroutine ); require Exporter; *import = \&Exporter::import; } # Put other "use" statements here, if any. sub testroutine { return "This is a test"; } 1;
      Thank you, that answers all my questions. So if package is omitted from my module it just treats the subroutine as if it's in the Main:: namespace?
      Thanks
      s;;5776?12321=10609$d=9409:12100$xx;;s;(\d*);push @_,$1;eg;map{print chr(sqrt($_))."\n"} @_;

        Not quite. It uses the currently "active" namespace.

        package Foo::Bar; use MyTest;
        would use Foo::Bar.
      Just a question to your answer - is there something wrong with this form?
      package MyTest; use strict; use warnings; use base qw(Exporter); our @EXPORT_OK = qw(testroutine); # Rest of the code here ...

      In other words, does BEGIN form have any advantage over use base?

      -BR
        • You pretend there's an is-a relationship where there isn't one.
        • That won't work if two modules include each other.

        Newer versions of Exporter recognize the first point and allow you to do

        package MyTest; use strict; use warnings; BEGIN { our @EXPORT_OK = qw( testroutine ); } use Exporter qw( import ); # Rest of the code here ...

        The BEGIN is there to address the second point.

Re: Use, Exporter, I'm Dizzy.
by ferreira (Chaplain) on Feb 07, 2007 at 17:16 UTC

    As a wild guess, I think your sin was to use a common name like test. If I look into my environment for a Perl module named test, I got:

    $ which_pm test test 1.25 /usr/local/lib/perl5/5.8.8/test.pm

    To tell the truth, it is not exactly test, but the Test module. But this is Cygwin running over a case-insensitive Windows file system, and the Perl interpreter is satisfied with this answer here. (I think the same will happen with other Perls running over Windows OSes.)

    If I say use test and no "test.pm" is there to be loaded, the one inside the core will make the loading succeed, but this is not what I intented, right? When you told explicitly to include "." in the @INC, your module has been found. So, a good advice is to not name your modules so that they trump over core modules (and take into account the fact that many filesystems out there are case insensitive).

Re: Use, Exporter, I'm Dizzy.
by gaal (Parson) on Feb 07, 2007 at 17:17 UTC
    Does any of the directories in your @INC contain a file test.pm?

    Try putting this in your script:

    END { print $INC{"test.pm"} }

    and commenting out the line that triggers the error. This will tell you which test.pm was loaded. (Note that Test.pm is a standard module! If you are on Windows, maybe that's being picked up. Its filesystem is incasitive.)

      Thank you for your response, that answers one of my questions. I renamed test.pm to test2.pm and my code now looks like this:
      #! /usr/bin/perl use warnings; use strict; use test2(); my $output = testroutine(); print $output, "\n";
      Outputs...
      $ ./test.pl This is a test
      However, how come I'm even able to call testroutine? I didn't think that use would populate my name space? Is it because I don't have package defined at the top of the module? Therefore it's assuming the subroutine is part of Main? Hope that makes sense.
      Thanks again,
      s;;5776?12321=10609$d=9409:12100$xx;;s;(\d*);push @_,$1;eg;map{print chr(sqrt($_))."\n"} @_;
        Is it because I don't have package defined at the top of the module? Therefore it's assuming the subroutine is part of Main?
        Yes. All the () on use Foo () does is suppress calling Foo->import - when the module directly defines things in the main package, or exports things in its toplevel code, that's not affected.

        I didn't think that use would populate my name space?

        Your module didn't tell perl which namespace to use, so perl placed your symbols in the currently active namespace (main). The fix is to specify a package in your module. See my post for details.

        Now that depends on the code in "test2.pm" which you have not shown up. If you're using Exporter, conventionally a statement such as

        use test2 ();

        does not export anything into the callee's namespace. You should be doing something weird and then getting weird behavior back. Take a look at the code written by ikegami at Re: Use, Exporter, I'm Dizzy. to see how the exporting code of your module should look like.

Re: Use, Exporter, I'm Dizzy.
by dirving (Friar) on Feb 07, 2007 at 17:29 UTC

    My guess for why the first two examples fail when the one with the use lib succeeds is that you must have another 'test.pm' somewhere in your library path. When you use lib it adds the directory to the beginning of the search path, so your test.pm is found before the other test.pm.

    As far as why your module exports the function when you specify the empty list on your use line: In your module you don't have a package declaration, so all the code is read into the main package. To get the behavior you expect your module would have to look something like this:

    package test; use base 'Exporter'; @EXPORT = qw/testroutine/; sub testroutine { 'This is a test'; } 1;

    See Exporter for more about how you can control what subroutines your class exports.

    -- David Irving
Re: Use, Exporter, I'm Dizzy.
by johngg (Canon) on Feb 07, 2007 at 19:24 UTC
    Complete side issue, your

    $ perl -e 'map {print $_,"\n"} @INC'

    could be more simply written as

    $ perl -le 'print for @INC'

    Cheers,

    JohnGG

      There are also a number of intermediary solutions.

      perl -e 'map {print $_,"\n"} @INC' perl -e 'print map "$_\n", @INC' perl -e 'print "$_\n" for @INC' perl -le '$,=$\; print @INC' perl -le 'print for @INC'
Re: Use, Exporter, I'm Dizzy.
by Anonymous Monk on Feb 07, 2007 at 18:16 UTC