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 ($@); 1The 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 ($@); 1Why 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 20Now 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 usingeval
followed by anothereval
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 byuse vars
.
[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"; 1So 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.
Very informative. I was having trouble while attempting to do a very similar task.
Thanks, I ran into a similar situation and this post really clarified the problem.
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”.
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.