in reply to lexicaly scoped export of variables?

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

Replies are listed 'Best First'.
Re^2: lexicaly scoped export of variables?
by LanX (Saint) on Mar 31, 2014 at 19:27 UTC
    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
Re^2: lexicaly scoped export of variables? (macro)
by LanX (Saint) on Mar 31, 2014 at 19:23 UTC
    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
        > Keyword::Simple

        You know, I met Lukas last week... :)

        This looks already pretty close, awesome!

        Pity we didn't have more time to talk.

        Cheers Rolf

        ( addicted to the Perl Programming Language)