http://qs321.pair.com?node_id=95813

Having finally grown comfortable with my own understanding of lexical and package variables(oh, and local too), I figured I'd take a shot at sharing my understanding with the average newbie that may be struggling with these concepts.

First off, I'd like to say that it was Damian Conway's Book, Object-Oriented Perl, that cleared things up with his explanation. Incidentally, this book can be purchases right through Perlmonks, by going here. And with that...



Package Variables
Package variables all belong to a package, even if only the main package. They can be called from anywhere within this package at any time, including sub-routines which may have been called from within that package.

#!/usr/bin/perl

$var = "foo";
print $var, "\n";

modify_var();
print $var, "\n";

sub modify_var {
    $var = "bar";
}

___OUTPUT___
foo
bar

The output from this code demonstrates the ability of package variables to be accessed anywhere in the current package. $var is being accessed in both the main body of the program, and in the modify_var sub-routine.

In cases where package ownership is important, such as when strict is being used, the package and double colon can be appended after the prefix symbol($,@,%) and before the variable name:

#!/usr/bin/perl

use strict;

$main::var = "foo";
print $main::var, "\n";

modify_var();
print $main::var, "\n";

sub modify_var {
    $main::var = "bar";
}

NOTE: When strict is in use and the package name is not used to fully qualify the variable, this error will be reported:
Global symbol "$var" requires explicit package name at test.pl line n.
Execution of test.pl aborted due to compilation errors.
Where n is the line number of the innappropriately accessed variable. Declaring the variable with the my qualifier will fix the problem also. This will create a lexical variable, which will be talked about later.

In cases where the main package is in ownership of the variable, the actual word 'main' can be ommitted to yield something like: $::var

UPDATE: One more point on Package variables. It is possible to get around having to fully qualify variable names when strict is in use. Applying a simply use vars to your script, with the variable names as it arguments will get around explicit package names. Example:

#!/usr/bin/perl use strict; use vars qw($foo); $foo = "bar"; print $foo, "\n";

Lexical Variables
Lexical variables must be explicitly declared using the my qualifier. Lexical variables belong to no package so cannot be fully qualified with a package name. They can be accessed only within the current block, or, only within the curly braces or file scope in which they are declared. They cannot be accessed by sub-routines called from within the current code block.

Let's look at the first code example, but use lexical variables where previously there were package variables:

#!/usr/bin/perl

my $var = "foo";
print $var, "\n";

modify_var($var);
print $var, "\n";

sub modify_var {
    my $var = shift;
    $var = "bar";
}

___OUTPUT___
foo
foo
Output from this code demonstrates that localization of the lexical variables. $var in the main package is treated as a completely different variable than $var in the modify_var sub-routine.

As was previously noted, using the my qualifier when strict is in use will prevent the Global symbol "$var" requires explicit package name at test.pl line n. error. This is because once a variable has been declared lexical, it belongs to no package and does not exist in the package's symbol table. So, it cannot be fully qualified.


NOTE: I said before that lexical variables are destroyed when the code block in which they are declared ends. This is usually the case. However, when a reference to the lexical variable continues to exist outside of the code block, the lexical variable survives past the block. It will be destroyed as soon as all references to it are gone. This is due to a Perl behavior known as reference counting.

How Does local Fit In?
Truthfully, at this point, it doesn't seem to. I've read in another node that local is useful in ammending code written in Perl4, or when you want to modify one of Perl special variables(see man perlvar) such as $/.

In any case, a little bit on how local works.

local takes a package(global) variable and temporarily replaces its value for the duration of the block in which it is used, meanwhile storing the old value to be replaced when the block ends. Here's an example:

$var = "foo";
print $var, "\n";

if (1) {
    local $var = "bar";
    print $var, "\n";
}

print $var, "\n";

___OUTPUT___
foo
bar
foo
Check the output out. Inside the if block, $var is declared using local and initialized to "bar". So for the duration of that block $var is storing "bar". When the if block ends, the previous value "foo" is restored to $var.

When I was playing around with all this stuff, I realized that using my and lexical variables externally behaves the same way as local, that is, it only seems like it does. Internally they behave very differently. The point is, though, that using lexicals can get the desired "temporary value" result.

When Should I Use Package Variables, and When Should I Use Lexicals?
Package variables are okay for short or uncomplicated Perl scripts, when you don't have to worry about intruding on the variable namespace of other routines or packages. Lexical variables should be used for large more complex programs. In truth, I always use lexical variables at this point, just out of habit and because I'm always using strict.


That's it for me. There's my first tutorial. I hope it helps.

Replies are listed 'Best First'.
Re: Lexical vs. Package Variables (With a little local thrown in)
by ant9000 (Monk) on Jun 20, 2003 at 08:28 UTC
    I find your explanations nice and clear; maybe you could also append the output of your scripts, just to make your points even easier to grasp.
    A note about local: it's real use is for temporarily overwriting Perl's automatic variables, which cannot be redeclared as lexicals, for instance
    #here $/ is usually a newline (on Unix, at least) { #localize the input record separator and undefine it local $/=undef; #now reading from STDIN yelds the entire contents at once my $whole_file=<>; #do what you like with lexical $whole_file #... } #here $/ is restored back to its original value