> 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.
The right way is to export the accessors to the variable rather than the variable itself. This makes local useless, so we have to find a different way. For example, we can use Scope::Guard:
andpackage Dsay; use warnings; use strict; use feature qw{ say }; use experimental qw( signatures ); use Scope::Guard qw{ guard }; use Exporter qw{ import }; our @EXPORT = qw( %debug set_darea get_darea dsay ); our %debug; my $d_area = '(default)'; # Not ours anymore! sub set_darea($value) { my $old_value = $d_area; $d_area = $value; return guard { $d_area = $old_value } } sub get_darea() { $d_area } # Etc.
Note that if you don't store the result of set_area to a variable, the module will complain:sub common_sub { say 'call to common sub (d_area is ', get_darea(), ')'; dsay "debugging output from common_sub"; } sub one { say "call to one:"; my $_scope = set_darea('one'); dsay "debugging output from one"; common_sub; dsay "more debugging output from one"; } sub two { say "call to two:"; my $_scope = set_darea('two'); dsay "debugging output from two"; common_sub; dsay "more debugging output from two"; }
Can't create a Scope::Guard in void context
Aside from doing it the right way, you can create the alias to the package variable yourself:
use Dsay; package Dsay; our $d_area; package main;
or
*d_area = *Dsay::d_area;
which can be done by extending Exporter's import:
use Exporter; our @EXPORT = qw( %debug dsay $d_area ); our %debug; our $d_area = "(default)"; sub import { my $caller = caller; no strict 'refs'; *{ $caller . '::d_area' } = *Dsay::d_area; goto &Exporter::import }
Interestingly, using something other than a scalar works the way you wanted.
anduse Exporter qw{ import }; our @EXPORT = qw( %debug %opt dsay ); our %debug; our %opt = (d_area => "(default)"); sub dsay( @args ) { return unless $debug{ uc $opt{d_area} } || $debug{'ALL'}; my $prefix = $opt{d_area} ne "(default)" ? "DEBUG $opt{d_area}: " : "DEBUG: "; say $prefix, @args; }
sub one { say "call to one:"; local $opt{d_area} = "one"; dsay "debugging output from one"; common_sub; dsay "more debugging output from one"; } # Etc.
Update: The reasons for this are unclear to me (maybe local for hash values was implemented later than local for variables, so there are probably some differences in the implementation).
Update 2: Extending the import method added.
Update 3: In fact, what exporter does is quite similar:
and if you try the same instead in the redefined import method, it doesn't work either. There's something more than the scalar slot that needs to be aliased, not sure why. The following works, too:*{"${callpkg}::$sym"} = $type eq '&' ? \&{"${pkg}::$sym"} : $type eq '$' ? \${"${pkg}::$sym"} : $type eq '@' ? \@{"${pkg}::$sym"} : $type eq '%' ? \%{"${pkg}::$sym"} : $type eq '*' ? *{"${pkg}::$sym"} : do { require Carp; Carp::croak("Can't export symbol: $type +$sym") };
*{ $caller . '::d_area' } = 'Dsay::d_area';
Update 4: In fact, adding the following line after Exporter::Heavy's block seems to fix the behaviour of Exporter without breaking anything I have tried:
*{"${callpkg}::$sym"} = "${pkg}::$sym" if '$' eq $type;
In reply to Re: 'local' for imported variables doesn't work as expected (4 updates)
by choroba
in thread 'local' for imported variables doesn't work as expected
by muthm
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |