Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the reason for the warning that a lexical variable is "not available" within eval

When an eval statement is within the scope of a lexical variable, that variable should be within the lexical context of the evaluated block. Moreover, lexical variables should be available in the lexical context of subs.

Yet this doesn't work:

use warnings;

{
    package VLE;

    my $ln10 = 2.302585092994045684017991454684;

    sub reval {
        say eval $_[0] if @_;
    }
}

package VLE;

reval( q($ln10) );

It results in:

Variable "$ln10" is not available at (eval 1) line 1.

But if I (uselessly) use the lexical variable anywhere in the block, it's suddenly available in the eval:

use warnings;

{
    package VLE;

    my $ln10 = 2.302585092994045684017991454684;

    sub reval {
        say eval $_[0] if @_;
        my (undef) = $ln10;
        return 0
    }
}

package VLE;

reval( q($ln10) );

prints

2.30258509299405

Why does this happen?

Edit:

The destruction of references isn't the issue, since this code (which maintains a reference to $ln10) also fails:

use warnings;

{
    package VLE;

    my $ln10 = 2.302585092994045684017991454684;

    sub reval2 {
        say eval $_[0] if @_;
        my (undef) = $ln10;
        return 0
    }

    sub reval {
        say eval $_[0] if @_;
        return 0
    }
}

package VLE;

reval( q($ln10) ); # still fails
reval2( q($ln10) ); # works
like image 682
alexchandel Avatar asked Oct 05 '17 15:10

alexchandel


1 Answers

A subroutine does not close over (capture) all visible lexical variables, but only those that are referred to within the subroutine body. This is a pretty integral part for reference counting to work properly. Which variables are captured is determined at compile time, and the variables are captured when the subroutine definition is executed (compile time for named subs, run time for anonymous subs).

Here your first reval does not capture the $ln10 variable. Therefore, it is not available inside the eval. Because the eval string is a runtime value, it cannot be taken into account when determining which variables should be captured.

More precisely, the $ln10 variable is destroyed when the enclosing block is left, because no further references to it exist. So this code would work because the $ln10 variable still exists while the eval is executed:

use warnings;
{
    package VLE;
    my $ln10 = 2.302585092994045684017991454684;

    sub reval {
        say eval $_[0] if @_;
    }

    reval(q($ln10));
    # $ln10 variable exists until here
}

The second eval does capture the variable so its lifetime is extended until the point in time where the eval is executed, and everything works as expected.

like image 197
amon Avatar answered Nov 15 '22 13:11

amon