Perl: use Module @list

perl, perl-module

Solution

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

Problem

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 `@EXPORT`ed, 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?

Original source