March 18, 2003

Perl scoping

OK, so I’ve just had a long discussion about why this breaks:

[darkness@darkbook darkness]$ perl -Mstrict -w
eval 'package Foo; our $bar = 1';  die if ($@);
eval 'package Foo; print "$barn"';  die if ($@);
Variable "$bar" is not imported at (eval 2) line 1.
Global symbol "$bar" requires explicit package name at (eval 2) line 1.
	...propagated at - line 2.
Turning off strict, of course, makes everything better:
[darkness@darkbook darkness]$ perl -w
eval 'package Foo; our $bar = 1';  die if ($@);
eval 'package Foo; print "$barn"';  die if ($@);
1
The solution to make this work is to use use vars instead of our it seems:
[darkness@darkbook darkness]$ perl -w -Mstrict
eval 'package Foo; use vars qw($bar); $bar = 1';  die if ($@);
eval 'package Foo; print "$barn"';  die if ($@);
1
Why is this? It is because Perl’s scoping rules were apparently written by a commission of Schizophrenic toddlers coming down off of a two week bender, and later edited by Charles Manson and the Jackson 5. This excerpt from perldoc -f our is helpful:
       our EXPR
               An "our" declares the listed variables to be valid
               globals within the enclosing block, file, or
               "eval".  That is, it has the same scoping rules as
               a "my" declaration, but does not create a local
               variable.
That page also has two examples, one of which I found particularly interesting:
               An "our" declaration declares a global variable
               that will be visible across its entire lexical
               scope, even across package boundaries.  The pack�
               age in which the variable is entered is determined
               at the point of the declaration, not at the point
               of use.  This means the following behavior holds:

                   package Foo;
                   our $bar;           # declares $Foo::bar for rest of lexical scope
                   $bar = 20;

                   package Bar;
                   print $bar;         # prints 20
Now check out the top of perldoc vars, as in use vars (I’m on Perl 5.6.1 BTW):
       NOTE: The functionality provided by this pragma has been
       superseded by "our" declarations, available in Perl v5.6.0
       or later.  See the our entry in the perlfunc manpage.
My ass our provides the same functionality, as we’ve demonstrated above. For this reason I’ve decided on the following concepts of Perl scopes:
Lexical scope
Best given in the quote from perldoc -f our, this scope is either a “block, file, or ‘eval’.” This is why my original attempt using eval followed by another eval failed: the variable was being declared in two different lexical scopes.
Package global scope
This scope appears to only be modified by use vars. If anyone else can give me a better example, I’m listening. These variables are visible using the fully-qualified variable name, $namespace::variable. The above example which “crosses package boundaries” doesn’t work with a variable declared by use vars.
There was this third scope I had decided existed, which I called the “package local scope.” I said this because of the following:
[darkness@darkbook darkness]$ perl -Mstrict -w
package Foo;
my $bar = 1;
package Blee;
print "$Foo::barn";
Name "Foo::bar" used only once: possible typo at - line 4.
Then I remembered the bit about lexical scoping, though, so check this out:
[darkness@darkbook darkness]$ perl -Mstrict -w
package Foo;
my $bar = 1;
package Blee;
print "$barn";
1
So there goes that notion: our “package local” is visible from outside the package. It’s local to the lexical scope. OK, so I guess that means that a variable declared with our is visible to code from outside the lexical scope (though you probably need a fully-qualified name?) whereas a variable declared with my is visible only to code that operates in the same lexical scope as the my declaration. Maybe? Now what the fuck does this mean:
[darkness@darkbook tmp]$ cat test1.pl
use strict;  use warnings;

our $foo = 123;
[darkness@darkbook tmp]$ cat test2.pl
use strict; use warnings;

require "test1.pl";
print "$main::foon";
[darkness@darkbook tmp]$ perl test2.pl
Name "main::foo" used only once: possible typo at test2.pl line 4.
123
[darkness@darkbook tmp]$ perl -pi -e 's/main:://g' test2.pl; cat test2.pl
use strict; use warnings;

require "test1.pl";
print "$foon";
[darkness@darkbook tmp]$ perl test2.pl
Global symbol "$foo" requires explicit package name at test2.pl line 4.
Execution of test2.pl aborted due to compilation errors.
While this might not match my articulation thus far, it matches the behavior my brain is currently expecting. My one problem is this: if you use strict but have two files that declare variables in the same package, you can’t easily access variables from file two when they’re declared in file one. I guess it just seems fucked up. With use warnings in effect you can’t even use the fully-qualified name without getting some complaints. Moral of the story: use package statements for every file that you’re going to want to access members of later. I’ll go ahead and throw in the added bonus: you shouldn’t use $a and $b in Perl code. Ever. This is because sort uses them:
               If you're using strict, you must not declare $a
               and $b as lexicals.  They are package globals.
               That means if you're in the "main" package and
               type

                   @articles = sort {$b <=> $a} @files;

               then "$a" and "$b" are "$main::a" and "$main::b"
               (or "$::a" and "$::b"), but if you're in the
               "FooPack" package, it's the same as typing

                   @articles = sort {$FooPack::b <=> $FooPack::a} @files;
I was doing my original tests with $a and $b which resulted in weird behavior like this:
[darkness@darkbook tmp]$ perl -Mstrict -w
eval 'package Foo; our $a = 1';  die if ($@);
eval 'package Foo; print "$an"';  die if ($@);
1
[darkness@darkbook tmp]$ perl -Mstrict -w
eval 'package Foo; our %a = (1=>2)';  die if ($@);
eval 'package Foo; print "$a{1}n"';  die if ($@);
Variable "%a" is not imported at (eval 2) line 1.
Global symbol "%a" requires explicit package name at (eval 2) line 1.
	...propagated at - line 2.
Needless to say, this really fucked me up. I couldn’t figure out why $a worked but %a didn’t. Thankfully, tiResias figured that mystery out. Lots of people had suggestions about why I was doing this-or-that. Here’s something like what I’m really doing, proving why I needed to do eval:
while (reading a line from file into $line)
	{
		... do some stuff to $line ...
		eval "package $unique_package_name; $line";
	}
So I was making sure all statements from some user file get evaluated in some unique package name. This is part of my Config::PerlConfiguration class. Thanks to heller, tiResias, and Wainstead for their discussion, comments, and guidance.

Comments (4)

  1. November 3, 2007
    Jams Schneider said...

    Very informative. I was having trouble while attempting to do a very similar task.

  2. February 22, 2011
    sporky said...

    Thanks, I ran into a similar situation and this post really clarified the problem.

  3. October 20, 2011
    Thom Blake said...

    In order to make sense of the above, I have to assume that backslashes are being removed. For example, it looks like you’re trying to print “$barn” rather than “$bar followed by a newline”.

    • October 20, 2011
      darkness said...

      Yup, thanks Thom. I can’t currently figure it out but that “$barn” is indeed supposed to be “$bar\n”. WP is eating my backslashes and I don’t currently know why.