The source code for vars is not totally impenetrable. The first thing to recall is that use Foo qw(bar); is essentially the same thing as:
BEGIN{
require Foo;
Foo->import('bar');
}
And the brief explanation of what import() typically does, is it makes some thing available within the caller's namespace. It exports something. In the case of my example, it exports a subroutine (probably) named bar. In the case of use vars qw($foo), it exports into the caller's namespace a package global scalar variable named $foo.
Now let's look at the import function from vars, which is invoked when use vars qw(...) is called:
sub import {
# Determine the package name of the caller.
my $callpack = caller;
# Grab the list of symbols that need to be exported.
my (undef, @imports) = @_;
my ($sym, $ch);
foreach (@imports) {
# For each "var", find the sigil and the symbol.
if (($ch, $sym) = /^([\$\@\%\*\&])(.+)/) {
# If the symbol contains non-word characters...
if ($sym =~ /\W/) {
# time for a more-detailed check-up
if ($sym =~ /^\w+[[{].*[]}]$/) {
# We're being asked to declare an array or hash el
+ement. That's not cool.
require Carp;
Carp::croak("Can't declare individual elements of hash or
+array");
} elsif (warnings::enabled() and length($sym) == 1 and $sym !~
+ tr/a-zA-Z//) {
# If it's a special punctuation variable...
warnings::warn("No need to declare built-in vars");
} elsif (($^H &= strict::bits('vars'))) {
# If we're strict, disallow funky variable names.
require Carp;
Carp::croak("'$_' is not a valid variable name under stric
+t vars");
}
}
# Build up the fully-qualified symbol name.
$sym = "${callpack}::$sym" unless $sym =~ /::/;
# Vivify the symbol name within the appropriate package/na
+mespace.
*$sym =
( $ch eq "\$" ? \$$sym
: $ch eq "\@" ? \@$sym
: $ch eq "\%" ? \%$sym
: $ch eq "\*" ? \*$sym
: $ch eq "\&" ? \&$sym
: do {
require Carp;
Carp::croak("'$_' is not a valid variable name");
});
} else {
# This is odd... we weren't given a sigil.
require Carp;
Carp::croak("'$_' is not a valid variable name");
}
}
};
That's it... some heuristics to decide what the fully qualified variable name should be, and then a little bit of symbol table trickery to make it exist.
use vars is useful because it makes the variable exist so that strict 'vars' doesn't get angry. It's an early form of pre-declaration. Other forms of pre-declaration include my and our. The first one (my) creates a lexical variable. our creates a package global and makes it available without using a fully qualified name, within the lexical scope in which our was invoked. vars is mostly oblivious to lexical scoping. It creates package globals, that's it.
The BEGIN block is usually used to control the order in which things happen as compiletime gets underway. By using vars in a begin block, most likely the author intended to have it happen in a specific order. use already happens during compiletime, but the BEGIN block can be used to fine tune the sequence of compiletime events.
I don't usually do "disclaimers", but... The dark art of symbol table manipulation and compiletime manipulation is difficult to express both in layman's terms and at the same time with sufficient precision as to not misrepresent anything. I apologize in advance if this post falls short in either endeavor.
|