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

I've run into a very odd scenario where scoping seems to break something awful. Consider the following examples:
File driver.pl: use strict; use TestMeNow; my %map = TestMeNow::initialize('value', 'Here I am!'); &{$map{'onecase'}{'ex'}}(); File TestMeNow.pm: use strict; package TestMeNow; my(%map, $value, %mapping); $map{'value'} = \$value; sub initialize { my(%args) = @_; foreach(keys %args) { ${$map{$_}} = $args{$_}; } my $string = <DATA>; %mapping = eval $string; return %mapping; } 1; __DATA__ 'onecase', { 'ex' => sub {print "$value\n";} }
This is an initialization routine I'm using as part of allowing *trusted* user configuration for a script (cut down to see the problem, obviously). Run as is, a newline is output. Adding the line  $value; immediately prior to the eval leads to the correct result, however. I've found a workaround, but re-reading perlsub leads me to believe this is broken behavior. Has anyone else seen anything similar? This is 5.8.4, running on Linux. Thanks, Cambo

Replies are listed 'Best First'.
Re: File-scoped lexicals and eval scope?
by dave_the_m (Monsignor) on Nov 08, 2007 at 23:28 UTC
    There is an implicit {} block round any file. When you use TestMeNow, the code in the file is compiled and executed, then at the end of execution, %map and $value go out of scope and are freed. Later you call initialize(), which within the eval, attempts to find the current value of $value and gets undef. In perl 5.10.0-to-be, if you enable warnings you get the following warning when that happens:
    Variable "$value" is not available at (eval 1) line 1, <DATA> line 1.

    Dave.

      %map and $value go out of scope, but are not freed, since initialize closes over %map which references $value. But since the implicit file scope was exited and initialize does not close over $value, in the call to initialize, $value is no longer bound to initialize's %map's ${$map{value}} and so is "not available". Correct?

      Inserting a $value if 0; into sub initialize to make it close over $value and correcting the 'ex'/'example' inconsistency makes it work (both on 5.8.x and 5.10).

        Good analysis on what was happening to $value.

        But if the OP wants persistent, file-scope variables, I'd say that package variables are a better choice.

        Changing the 'my' to 'our' would arrange that and be more readable than relying on tricks like 'if 0', imho.

Re: File-scoped lexicals and eval scope?
by moritz (Cardinal) on Nov 08, 2007 at 23:04 UTC
    'ex' ne 'example'

    Are you sure you are calling the sub that you think you are calling?

    Update: Now I actually tested the code (and not just looked at it), and all I get is this message (with perl 5.8.8):

    Can't use string ("") as a subroutine ref while "strict refs" in use at driver.pl line 4, <DATA> line 1.</c>

    Are you sure that you get a newline as output if you run exactly that script?

      Yeah, I ended up with a typo in what I posted, when I was trying to clean it up for public consumption. Obviously, running the code I posted would blow up. It probably doesn't do any good asking for help on this sort of problem when my example doesn't run. I'll try to be more pedantic next time :) Cambo