I have tried to boil this behavior down to the simplest test case. Consider the following two modules:
package Bar;
use base 'Exporter';
use vars qw/ $BarVar /;
BEGIN { @EXPORT_OK = qw/ $BarVar /; }
$BarVar = 'original';
1;
package Foo;
use Bar qw/ $BarVar /;
sub foo { print $BarVar . "\n"}
1;
Now, the output of the following script -
use strict;
use warnings;
use Foo;
{
local $Bar::BarVar = 'modified';
Foo::foo();
}
Foo::foo();
is "original" printed twice, where I would have expected it to be "modified" followed by "original", because I would have expected the local
declaration to replace the package variable $Bar::BarVar
throughout its whole scope, which includes the first call to foo()
. What's the explanation? How can I locally override $Bar::BarVar
?
You may have heard me say that my
and our
create variables (the latter of which is aliased), but that local
just makes a temporary backup of the value.
I lied.
local
does make a backup, but not of the value. It backs up the address of the associated scalar, creates a new scalar, and associates the name with the new scalar. In this case, that leaves $Bar::BarVar
referring to the new scalar, and $Foo::BarVar
referring to the old scalar.
$ perl -E'
*x = \$y; say \$x, " - ", \$y;
local $y; say \$x, " - ", \$y;
'
SCALAR(0x44d7c70) - SCALAR(0x44d7c70)
SCALAR(0x44d7c70) - SCALAR(0x44ba130)
If you were to actually backup just the value, the problem would go away.
use Sub::ScopeFinalizer qw( scope_finalizer );
{
my $backup = $Bar::BarVar;
my $guard = scope_finalizer { $Bar::BarVar = $backup };
$Bar::BarVar = 'modified';
Foo::foo();
}
A more specialized tool might exist.
See What Not to Export:
There's one more item to add to this list. Do not export variable names. Just because Exporter lets you do that, it does not mean you should.
@EXPORT_OK = qw($svar @avar %hvar); # DON'T!
Exporting variables is not a good idea. They can change under the hood, provoking horrible effects at-a-distance that are too hard to track and to fix. Trust me: they are not worth it.
To provide the capability to set/get class-wide settings, it is best instead to provide accessors as subroutines or class methods instead.
There is one exception this rule: If you have constant variables (such those created using Const::Fast), you can export them because, by definition, they can't create effects at a distance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With