Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: use Module @list

Someone has an idea to use array variable in place of array (list) literal, in the use function statement, like:

my @list = qw(foo zoo);
use Module @list;

instead of

use Module qw(foo zoo);

So she writes e.g.:

my @consts = qw(PF_INET PF_INET6);
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;

which seemingly works as expected:

2, 10

Then she is doing it with some other module, e.g. Time::HiRes. Instead of

use Time::HiRes qw(CLOCK_REALTIME CLOCK_MONOTONIC);
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

0, 1

she does:

my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
use Time::HiRes @consts;
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

0, 0

It suddenly does not work, like it worked with Socket module! Something bad is happening here.

(.. it's in non-strict environment. If she used use strict, she would even have gotten an error. On the other hand, she gets no hint at all in her first seemingly working example - even when she has use strict; use warnings; use diagnostics there.)

Now she wants to explore this weird behavior. Tries importing an empty list:

my @consts = ();
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;

2, 10

surprisingly works as well, while it probably should not, like:

use Socket ();
printf "%d, %d\n", PF_INET, PF_INET6;

0, 0

Then she digs a little into those modules a little and realizes, that that the difference between the two modules is that these constants are / are not @EXPORTed, respectively.

Her conclusion is that the use Module @list does not work as she is expecting.

What would be the best explanation for that? What is she doing wrong - What is the correct way of using pre-defined array in the use statement?

like image 556
mykhal Avatar asked Dec 05 '13 09:12

mykhal


People also ask

How do I run a pm file in Perl?

Install the module properly using cpanm (or cpan or another CPAN installer). If you are using ActiveState Perl, then you probably should look to its PPM installer. on the command line. Actually, it can be any directory in the @INC list.

How do I load a Perl module?

Try search.cpan.org/~bingos/Module-Load-0.32/lib/Module/Load.pm, or maybe even perldoc.perl.org/Module/Load.html.

What is cpan module in Perl?

The Comprehensive Perl Archive Network (CPAN) is a repository of over 250,000 software modules and accompanying documentation for 39,000 distributions, written in the Perl programming language by over 12,000 contributors.


1 Answers

This has to do with when the code is executed. use is executed at compile time, while the my @list is only executed at runtime. So the array does not exist a the point the module is loaded.

The module Socket exports PF_INET and PF_INET6 by default, so it doesn't matter if you put it in the use line. But Time::HiRes does not export stuff by default.

The error you get with strict is:

Bareword "CLOCK_REALTIME" not allowed while "strict subs" in use ...

That tells us that Perl does not know that CLOCK_REALTIME is a sub, which is true, because it was not loaded when we do this:

my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
use Time::HiRes @consts;
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

What use does is require the module and import the LIST of arguments at compile time. So it's the same as:

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

Knowing that, we can do it ourselves:

use strict; use warnings;
BEGIN { 
  my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
  require Time::HiRes;
  Time::HiRes->import(@consts);
}

printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

__END__
0, 1

Like this it will work, because the array @const is defined in the same scope and is already available when the Perl interpreter executes it.

Because of scoping, just adding a BEGIN block in front of the use will not work.

BEGIN {
  my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
}

use Time::HiRes (@consts);

You can get around the problem by declaring the variable outside the BEGIN block. That way it will be available in the next BEGIN block's scope, and the value will already be set because the BEGIN blocks are executed at compile time in FIFO order.

my @consts;
BEGIN {
  @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
}

use Time::HiRes (@consts);
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

__END__
0, 1

So to recap:

  • it's because of two things: scope and order of execution
  • the fact that you pass an array is not the issue here - the array will be part of the LIST that is passed
  • if you do not use strict, you cannot easily find the problem
  • if you add a BEGIN block in front of the use and place the my declaration outside the BEGIN, it works
  • if you take require instead of use and import yourself, you can also pass an array
like image 119
simbabque Avatar answered Oct 19 '22 13:10

simbabque