How does the Perl 'use' syntax work?

2019-06-12 21:57发布

问题:

Sample code:

m1.pm

my $a;
my $b;
sub init {
    $a = shift;
    $b = shift;
}

sub printab {
    print "a = -$a-\n";
    print "b = -$b-\n";
}

1;

m2.pm

my $a;
my $b;
sub init {
    $a = shift;
    $b = shift;
}

1;

test.pl

use strict;
use warnings;

use m1;
use m2;

init('hello', 'world');
printab();

Run:

$ perl test.pl
a = --
b = --
$

What happens is that the init('hello', 'world') call is mapped to m2.pm and initializes the variables ($a and $b) there.

This kind of makes sense, but what I do not understand is why those values are not available in test.pl.

  • Is there something fundamentally wrong that I am trying to do here? What is the correct way to use two modules with same named subroutines and variables?

  • How exactly does a Perl use work? It would help if someone could contrast it with C's #include directive.

回答1:

First, do read perldoc perlmod.

You do not declare a namespace in either module, so everything is in the main namespace. Declare package m1; in m1.pm and package m2; in m2.pm.

At the very least, you should implement an import method (or inherit the one Exporter provides) so that programs that use modules can decide what to import from where.

It also seems to me that you are exploring around the edges of OO.

Further:

  • Avoid using $a and $b as variable names because it is easy to confuse them with the package variables $a and $b used by sort.

  • Don't use lower case module names: They are reserved for pragmata.

A minimal implementation (all in one file for testing convenience) looks like this:

package My::M1;

use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub a {
    my $self = shift;
    my ($v) = @_;
    $self->{a} = $v if @_;
    return $self->{a};
}

sub b {
    my $self = shift;
    my ($v) = @_;
    $self->{b} = $v if @_;
    return $self->{b};
}

package My::M2;

use strict; use warnings;
use base 'My::M1';

sub printtab {
    my $self = shift;
    for my $x (qw(a b)) {
        printf "%s = -%s-\n", $x, $self->$x;
    }
}

package main;

my $m = My::M2->new(a => 'hello', 'b' => 'world');
$m->printtab;


回答2:

In Perl, the use keyword is exactly equivalent to the following:

use Mymodule;

#is the same as

BEGIN {
   require Mymodule;
   Mymodule->import();
}

So if you are not defining an import routine in your code (or inheriting from Exporter), then your modules are not importing anything into test.pl

As Sinan caught, you are not declaring a package in your modules, so they are defaulting to the main package. In that case, all of your subroutines are in main, but the lexical variables (declared with my) are only scoped to the file they are declared in.

So m1 defines sub init and sub printab to which the lexicals $a and $b are in scope. But then when test.pl loads m2, the init routine is overwritten with the new definition, which is not closed around the two lexicals anymore. So it is writing to the package variables $main::a and $main::b instead of the lexicals which printab is bound to.

If you had warnings enabled (which you always should when learning), you would have been warned about the subroutine redefinition.

You should start each of your modules with:

package Some::Package::Name;
use warnings;
use strict;

and then end each module with:

1;

This is because when you use/require a module, it needs to return a true value at the end so that Perl knows it loaded properly.



回答3:

printab() is defined in the file m1.pm and only has access to the $a and $b variables that are scoped to that file. The variables $a and $b in m2.pm are scoped to that file, and they are different variables than the $a and $b in m1.pm.

init() sets the variables scoped in m2.pm (because that's the last place the &init function was defined) so it is not setting the same variables that printab() will be trying to print.