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.