in reply to Accessing lexicals in other scopes dynamically by name

I didn't expect that it's possible to dynamically access a lexical var by using eval

It would be a bit silly if one couldn't do

my $greet = "Hello World"; eval ' print "$greet\n"; ';

eval doesn't know how its argument was generated, so

eval 'print "$gr' . 'eet\n";';
and
my $name = $greet; eval 'print "$' . $name . '\n";';
are both equivalent to the first.

Note: Subs only capture variables it knows it will need.

$ perl -E' use warnings; use strict; sub f { my $n="ok"; sub { eval q{$n} } } say f->() ' Variable "$n" is not available at (eval 1) line 2. Use of uninitialized value in say at -e line 4. $ perl -E' use warnings; use strict; sub f { my $n="ok"; sub { $n; eval q{$n} } } say f->() ' Useless use of private variable in void context at -e line 3. ok $ perl -E' use warnings; use strict; sub f { my $n="ok"; sub { $n if 0; eval q{$n} } } say f->() ' ok

Replies are listed 'Best First'.
Re^2: Accessing lexicals in other scopes dynamically by name
by LanX (Saint) on Jul 30, 2010 at 16:35 UTC
    > It would be a bit silly if one couldn't do

    Sure ... but...

    > Note: Subs only capture variables it knows it will need.

    which was exactly my intuition, you understand my confusion, since

    my enclosed sub from the OP:

    $getset=sub { my ($name,$val)=@_; eval('$'.$name."='".$val."'"); # I can access any variable in this + scope };

    doesn't know it will need to capture $x!

    So whats happening here?

    Cheers Rolf

      You never captured $x. It was simply still in scope. You basically did
      my $x="global"; my $test=sub { my $x="pre"; alert($x); # pre my ($name,$val)=@_; eval('$'.$name."='".$val."'"); alert($x); # post }
      which we've already established should work. Call the sub from outside the scope in which $x resides and you'll see it fail.
        >Call the sub from outside the scope in which $x resides and you'll see it fail.

        please define your understanding of "capture" and "scope" (dynamic or static).

        Cheers Rolf

      Some changes to your code to make it clearer!

      use strict; use warnings; my $n="outer"; my $f; { my $n="ok"; $f=sub { eval "".shift }; check(); # "ok" }; sub check { print $f->('$n') } print $n; # "outer" exit;

      Cheers Rolf

Re^2: Accessing lexicals in other scopes dynamically by name
by morgon (Priest) on Jul 30, 2010 at 16:49 UTC
    Note: Subs only capture variables it knows it will need.
    I don't think that is true.

    My understanding is that closures capture the whole environment (in the sense of lexical variable bindings).

    Otherwise this would not be possible:

    use strict; my $a = 1; my $b = 2; my $c = 3; sub f { my $var = shift; print $var, "=", eval "\$$var", "\n"; }; &f("a"); &f("b"); &f("c"); # prints a=1 b=2 c=3

      Nothing's captured. $a, $b, $c are still in scope, and we've already established that eval should and can access lexicals in scope.

      Place all by the calls to f() in curlies or in a module and you'll see it doesn't capture.

      use strict; { my $a = 1; my $b = 2; my $c = 3; sub f { my $var = shift; print $var, "=", eval "\$$var", "\n"; }; } &f("a"); &f("b"); &f("c");
      $ perl -w a.pl Variable "$a" is not available at (eval 1) line 2. Use of uninitialized value in print at a.pl line 11. a= Variable "$b" is not available at (eval 2) line 2. Use of uninitialized value in print at a.pl line 11. b= Variable "$c" is not available at (eval 3) line 2. Use of uninitialized value in print at a.pl line 11. c=
        ikegami, you are right.

        Does anyone have any idea why Perl does it like that?

        I mean to implement it like this must be more difficult than simply to keep a pointer to the whole enviroment for every closure.

        What is the motivation?

Re^2: Accessing lexicals in other scopes dynamically by name
by LanX (Saint) on Jul 30, 2010 at 17:01 UTC
    > Note: Subs only capture variables it knows it will need.

    I ran some tests, IMHO thats not right, the point is rather that at run time the variable doesn't exist any more in your example.

    Since it's only referenced by symbol, there is no reference count on the variable and the garbage collector already deleted it.

    Cheers Rolf

      the point is rather that at run time the variable doesn't exist any more in your example.

      Exactly. And the reason it doesn't exist is because it wasn't captured. That would have kept it alive. You can see the capture increasing the ref count in the following snippet:

      $ perl -MDevel::Peek -e' sub f { my $x; Dump($x); my @a = map { sub { $x } } 1..3; Dump($x); } f() ' SV = NULL(0x0) at 0x817bc90 REFCNT = 1 FLAGS = (PADMY) SV = NULL(0x0) at 0x817bc90 REFCNT = 4 FLAGS = (PADMY)
      I think ikegami is right after all.

      Consider:

      use strict; { my $a = \$a; print "a=$a\n"; sub f { my $var = shift; print $var, "=", eval "\$$var", "\n"; }; } &f("a");

      $a references itself, so it will never be garbage-collected, yet it is not accessible in the eval.

        Indeed it has nothing (or little) to do with garbage collection.

        IMHO it's the dynamic scope (i.e. the scope local has), eval can access the surrounding lexpads of f's closure only as long as there is a call chain connecting them, because the "capture" of $a is missing!

        use strict; { my $a = \$a; print "a=$a\n"; sub f { my $var = shift; print $var, "=", eval "\$$var", "\n"; }; i(); # works } #&f("a"); sub g { f("a") } sub h { g() } sub i { h() }

        Cheers Rolf