Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

What does use vars qw{$VERSION}; do?

by alwynpan (Acolyte)
on Oct 31, 2016 at 04:08 UTC ( [id://1174985]=perlquestion: print w/replies, xml ) Need Help??

alwynpan has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I am a Perl newbie and halfway going through the "Learning Perl" book. I am reading some code from a module, and just wondering what does the following code do.

use vars qw{$VERSION}; BEGIN { $VERSION = '0.92'; }

Thank you.

Replies are listed 'Best First'.
Re: What does use vars qw{$VERSION}; do?
by kevbot (Vicar) on Oct 31, 2016 at 04:33 UTC
    It creates a global variable named $VERSION. The use of a BEGIN block ensures the variable is set before other code in the script has run. See the documentation for BEGIN and use vars. Here is a nice introduction to BEGIN blocks. Note that use vars has been superseded by our. This use vars vs our vs $main:: for $VERSION node has a discussion that you may find helpful.
      Thank you for your prompt reply. So this will be the same as?
      our $VERSION; BEGIN { $VERSION = '0.92'; }
      But I can't see the variable $VERSION is used in this file, can I assume this is used in some other files? Sorry, this is my first day in the Perl world, my question may be very silly.

        All CPAN modules should (and many other non-CPAN modules also do) declare a $VERSION. This allows the caller to do this:

        use Foo; if (Foo->VERSION() < 1.56) { # do something... } # or if ($Foo::VERSION < 1.56) { # do something... }

        (Take your pick.)... or...

        use Foo 1.56 # Die during compiletime if the minimum version is not me +t.

        In most cases the module that declares a version doesn't use it for anything itself. The package global is set up for external consumption. This is one of the benefits of package globals: Anyone from any other package can reach right in and have a look.

        Here's another example:

        perl -MList::Util -E 'say List::Util->VERSION()'

        The BEGIN block, once again, is used to assure that the variable is given a value early enough in the compile process that it will be available to the caller when the caller invokes use. It also turns out that use can be used to enforce minimum versions, so the variable MUST have a value early in the process. See use for additional explanation of version checking, and perlmod and perlmodstyle for discussion of version numbers.


        Dave

        Yeah, you could use that instead (since Perl 5.6). There's a corner case in which they are different, but it's nothing to worry about.

Re: What does use vars qw{$VERSION}; do?
by davido (Cardinal) on Oct 31, 2016 at 05:09 UTC

    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.


    Dave

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1174985]
Approved by kevbot
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2024-04-25 05:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found