I have working code, but I am trying to understand why it works. I am also trying to learn more about the internals of Perl 5 (perlbrew
, perl-5.26.1
, Cygwin x64
).
I know from perlvar
and strict
that use strict 'vars'
works by setting flags in $^H
. Perl then tests accesses to non-::
variables based on those flags. Somehow, both our
and use vars
mark variables so that they will pass the test. How do they do so?
For example:
perl -E 'package Foo;
use strict "vars";
use vars qw($foo);
say $foo;'
runs fine (although it produces no output). Based on the source for use vars
, I tried this, which I thought would have the same effect:
perl -E 'package Foo;
use strict "vars";
my $sym = "Foo::foo"; # <-- These two lines pulled straight
*$sym = \$$sym; # <-- from the source for the vars pragma
say $foo;'
However, it gave me an error: Global symbol "$foo" requires explicit package name
. I also tried $sym = "::Foo:foo"
in the above, with the same result.
I checked, and $Foo::foo
is in the symbol table:
$ perl -E 'package Foo;
use Data::Dumper;
use strict "vars";
my $sym = "Foo::foo";
*$sym = \$$sym;
say Dumper(\%{"Foo::"});' # <-- Foo's symbol table
Output:
$VAR1 = {
'BEGIN' => *Foo::BEGIN,
'Dumper' => *Foo::Dumper,
'foo' => *Foo::foo # <-- yep, it's there
};
What else is use vars
doing that I'm missing? Does our
do the same, or something different?
Here's an A/B based on melpomene's answer:
Fails Succeeds
------------------------- ----------------------------------
package Foo; package Foo;
use strict "vars"; use strict "vars";
BEGIN {
package Bar;
my $sym="Foo::foo"; my $sym = "Foo::foo";
*$sym = \$$sym; *$sym = \$$sym;
}
say $foo; say $foo;
use strict 'vars'
works by setting flags in$^H
.
Yes, but that's an implementation detail. $^H
exposes some internal interpreter state bits, but you're not supposed to touch it in normal code.
Somehow, both
our
anduse vars
mark variables so that they will pass the test. How do they do so?
This is also considered an implementation detail.
However, we can peek a bit under the hood. strict "vars"
complains about undeclared variables (at compile time).
There is a hardcoded list of variables that are exempt from this check; it includes all punctuation variables (e.g. $/
, $_
, etc. along with $a
and $b
(used by sort
)).
All lexically (i.e. locally) declared variables also pass strict
; this is how my
, our
, and state
work. (For our purposes local
is not a declaration and does not create local variables; local
temporarily changes the value of an existing variable.)
The third exception is variables exported from modules. Using global variables as part of your module interface is generally considered to be a bad idea, but some older modules still do it. English
also exports variables because that's its whole point, so we'll use it as an example:
use strict;
use English qw($INPUT_RECORD_SEPARATOR);
$INPUT_RECORD_SEPARATOR = ""; # <--
my $paragraph = readline STDIN;
The line marked <--
does not throw an error because Perl remembers which variables were imported from a module.
What does "exporting" actually mean? It just means aliasing a symbol across package boundaries:
*main::foo = \$Some::Module::foo; # now $main::foo is an alias for $Some::Module::foo
The curious thing is that as far as the Perl internals are concerned, a variable is "imported" if it has been aliased in some other package. It does not matter what it was aliased to; all that matters is where the aliasing happened. use vars
(ab-)uses this detail to bypass strict "vars"
by exporting your own variables back at you:
package Some::Package;
use vars qw($foo);
works like
package Some::Package;
BEGIN {
package vars;
*Some::Package::foo = \$Some::Package::foo;
}
# now $foo is an alias to ... itself
The other piece of the puzzle is that use
happens at compile time, like BEGIN
blocks. Your example fails because your aliasing attempt only happens at runtime, which is too late for strict
, and because it doesn't switch to a different package to do the aliasing.
In the end vars
is just a module, written in plain Perl. our
is different: It is a real keyword and part of the language. It also has different behavior: It effectively creates an alias (to a package variable), but that alias lives in a local scope, not the symbol table.
Consider e.g. the following:
my $foo = 2;
{
our $foo = "hello";
print "foo = $foo; main::foo = $main::foo\n";
}
print "foo = $foo; main::foo = $main::foo\n";
This outputs
foo = hello; main::foo = hello
foo = 2; main::foo = hello
because the inner our $foo
declaration shadows the outer $foo
in the inner block. Within the block both $foo
and $main::foo
refer to the same variable; outside $foo
refers to the lexical my $foo
, which is untouched.
Another difference to use vars
:
use strict;
package Foo;
our $x = "hello";
package Bar;
print "$x\n"; # hello
This code works fine because package
declarations don't create a new scope. There is only one unit of scoping here (the whole file), and so our $x
makes $x
refer to $Foo::x
for the rest of the file, no matter which package you switch into.
On the other hand:
use strict;
package Foo;
use vars qw($x);
$x = "hello";
package Bar;
print "$x\n";
This code doesn't even compile. The reference to $x
in the last line can't be resolved: Perl checks the local scope first, but there are no locally declared $x
's. It then checks the current package (Bar
) and finds nothing either, and without strict "vars"
it would have automatically created $Bar::x
for you, but with strict "vars"
enabled this is simply an error. $Foo::x
is irrelevant and never checked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With