LanX has asked for the wisdom of the Perl Monks concerning the following question:

Hi

is there a way to use a package such that the exported variables only exist in the current scope?

(ideally lexical vars)

If not how would you name a module allowing this?

Cheers Rolf

( addicted to the Perl Programming Language)

Replies are listed 'Best First'.
Re: lexicaly scoped export of variables?
by tobyink (Canon) on Mar 30, 2014 at 09:06 UTC

    An interesting question.

    Firstly, let's discount exporting package variables. It's easy enough to do, and while they can't be lexically scoped, you can provide a dynamic-scoped value (similar to the effects of local). You'd just set up the variable in your import method, and use B::Hooks::EndOfScope to restore the old value at the end of the scope that imported you...

    use strict; use warnings; BEGIN { package MyVars; use Exporter::Shiny; use B::Hooks::EndOfScope; sub import { no strict "refs"; my $caller = caller; my $old_value = ${"$caller\::pi"}; *{"$caller\::pi"} = do { my $new_value = 3.14159; \$new_value +}; on_scope_end { *{"$caller\::pi"} = \$old_value; } } }; use Data::Dumper; { use MyVars; BEGIN { print Dumper($pi) } } BEGIN { print Dumper($pi) }

    However, note that the print statements in the importing package need to happen at compile time; this is because our dynamic definition of the variable only happens at compile time; by run time it's gone.

    So that's a useless idea. On to lexical variables.

    The tricky part is actually declaring the lexical variable in the caller's scope so that when they use the variable, it doesn't result in a compile-time error. This requires using parser hooks. B::Hooks::Parser is pretty reliable, and works on Perl 5.8+.

    use strict; use warnings; BEGIN { package MyVars; use Exporter::Shiny; use B::Hooks::Parser; our $pi = 3.14195; sub import { B::Hooks::Parser::inject(sprintf('my $pi; BEGIN { $pi = $%s::p +i };', __PACKAGE__)); } }; use Data::Dumper; my $pi = 3; { use MyVars; print Dumper($pi); } print Dumper($pi);

    With a little extra work (and Data::Alias), you could make the "exported" $pi into an alias for the $MyVars::pi package variable. Or you could make it read-only. Or a tied variable, or an overloaded object, or whatever.

    Update: I've packaged up this solution as Exporter::LexicalVars. I'll probably release a stable version in a few days.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      Thanks, I wanted to be sure that I'm not missing an obscure pragma.

      My approach is to declare the lexicals within the destination scope and to inspect the names in the importing package with PadWalker.

      Of course this doesn't help importing groups of lexicals so they have to be listed individually, but maybe just syntactic sugar is not the worst approach.

      use strict; use warnings; package bla; my ($x,$y)=(42,43); require export_var; do { export_var->import my ($x,$y) ; print $x,$y; } for 1..3; print $x,$y;

      use strict; use warnings; use feature 'say'; package export_var; use PadWalker qw[var_name]; use Data::Dump; my %value = ( '$x'=>1, '$y'=>2, ); sub import { my $pkg=shift; for my $var (@_) { $var = $value{ var_name(1,\$var) }; } }

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        PadWalker should certainly do the trick if you're happy to declare the variables with my in the target scope.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      Thanks a lot, thats fascinating.

      I can't put too much dependencies into it so I approached it with PadWalker. (see next post)

      But looking into your approach, I'd like to know if it's possible to hook into the parser to implement a macro mechanism like this.

      Like:

      a) parse call-statements like my_call(1,2,3)

      b) check if sub my_call is a macro

      c) if no continue as usual and create entersub opcode

      d) if yes execute macro my_call with parsed parameters as strings exp('1','2','3')

      e) inject returned string as code

      f) continue with next statement

      Is this feasible??? :)

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        "I'd like to know if it's possible to hook into the parser to implement a macro mechanism like this."

        You know, I was thinking something similar myself. Personally I'd do it with Parse::Keyword/Keyword::Simple.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: lexicaly scoped export of variables?
by Anonymous Monk on Mar 30, 2014 at 02:39 UTC
    see import and unimport and no and namespace::autoclean

    moose might unimport its stuff ... but strict is lexical like that (unimport)

    $ perl -e " use strict; BEGIN{eval q{use strict; $foo = 1;};warn$@;} n +o strict; $bar = 2; " Global symbol "$foo" requires explicit package name at (eval 5) line 1 +.
Re: lexicaly scoped export of variables?
by Anonymous Monk on Mar 30, 2014 at 13:09 UTC
    I think that the best answer is "no." Even if you did manage to cobble up something exotic by diving into the depths of Perl, someone after you will wind up having to maintain it.