Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Variable scoping outside subs

by xiper (Friar)
on Oct 27, 2003 at 22:46 UTC ( #302556=perlquestion: print w/replies, xml ) Need Help??

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

Can someone please explain the following behaviour, specifically line 6 and how/when it is evaluated?
1| use strict; 2| &test(); 3| # print "$var\n"; # Global symbol "$var" requires explicit... 4| exit; 5| 6| my $var = 1; 7| sub test 8| { 9| print "var was '$var'\n"; 10| $var++; 11| print "var is now '$var'\n"; 12| } 13| __END__ 14| var was '' 15| var is now '1'
Thanks.

Replies are listed 'Best First'.
Re: Variable scoping outside subs
by Roger (Parson) on Oct 27, 2003 at 23:44 UTC
    Line 6 is never evaluated.

    When you uncomment line 3, you get the following compilation (not run-time) error:
    Global symbol "$var" requires explicit package name at p03.pl line 3. Execution of p03.pl aborted due to compilation errors.
    Because you have use strict; turned on, the Perl interpreter checks that a variable is created with my keyword before it is used, during the compilation stage.

    If you move the line my $var = 1; to the end of the code (after sub test), you will get a compilation error.

    Back to your original code with line 3 commented out, when the Perl compiler gets to the line my $var = 1;, it create an entry in the package's global symbolic/variable table for $var, with the value being undef. And when the compiler gets to sub test later, it checks to see if $var exists in the symbol table, and found it. So the compilation succeeds.

    my $var = 1; has two components:
    my $var; # compile time component inserts variable into scratchpad # of the scope $var = 1; # run-time component assigns the value
    The value of the $var is set at run-time. Because line 6 is never executed, the initial value of $var is undef when you call the subroutine test.

    use strict; test(); #print "$var\n"; # Global symbol "$var" requires explicit... exit; my $var = 1; sub test { print "var was undef\n" if $var eq undef; print "var was '$var'\n"; $var++; print "var is now '$var'\n"; }
    And the output is -
    var was undef var was '' var is now '1'
    Updated: Thanks to welchavw for pointing out that the lexical variables are not stored in the package symbol tables. Instead, the lexical variables are stored in the scratchpad of the scope, file scope in this case.

      Roger,

      I really like most of your explanation, which is much clearer than my attempt, but I am not sure that I agree completely. Lexicals are not inserted into any symbol table as far as I know, so I think that portion of the explanation may be erroneous.

      ,welchavw

        Hi welchavw, thanks for pointing out that my variables are not inserted into symbol table, instead they are inserted into scratchpads. There are a few excellent references on this topic:

        1. Advanced Perl Programming (Oreilly)

        The my variables are inserted into the 'scratchpad' of the scope, not the package symbol table.

        2. Perl lexical my variables.

        The my operator declares the listed variables to be lexically confined to the enclosing block, ...

        ...

        This doesn't mean that a my variable declared in a statically enclosing lexical scope would be invisible. Only dynamic scopes are cut off. For example, the bumpx() function below has access to the lexical $x variable because both the my and the sub occurred at the same scope, presumably file scope.

        my $x = 10; sub bumpx { $x++ }
      Excellent reply, thanks! ++
      Minor nitpick... $var eq undef is the same as $var eq '':
      perl -e'$var="";print "ooh" if $var eq undef'

      - ><iper (who recently decided to log in...)

      use japh; print;
Re: Variable scoping outside subs
by welchavw (Pilgrim) on Oct 27, 2003 at 23:20 UTC

    Neat failure, thanks for posting. Here's a try at an explain. That $var is compiled prior to test(), thus test() can refer to it (you can't move $var after test()), acting as a closure, as has been noted (++). However, as other posters have noted, line 6 does not get executed, so the assignment is never performed. The value of $var is undefined when printed, as a result.

    Now, I actually rather believe that storage is allocated on-the-fly, during the call to test(), rather than at compilation-time.

    Also, here's some additional test code I wroteup just to check some of my thoughts.

    use strict; &f; #"pre=> ", "post=>2" my $v = 1; &f; #pre=>1", "post=>3" exit; sub f { print "pre=>$v\n"; $v += 2; print "post=>$v\n"; }
    update

    Here's some further code that was illustrative to me.

    #this code proves that scratchpads keep lexicals around, #not mere copies of those lexicals; something like reference #counting of lexicals is occuring with closures - the details #don't really matter - the $v lexical persists in both the f1 #and f2 closures, not a mere "copy" of $v. sub f_maker { my $v = 1; my $f1 = sub { print "f1_pre =>$v\n"; $v++; print "f1_post=>$v\n"; }; my $f2 = sub { print "f2_pre =>$v\n"; $v++; print "f2_post=>$v\n"; }; return ($f1, $f2); } my ($f1, $f2) = &f_maker; &$f1; &$f2; &$f1; __OUTPUT__ f1_pre =>1 f1_post=>2 f2_pre =>2 f2_post=>3 f1_pre =>3 f1_post=>4
Re: Variable scoping outside subs
by Zaxo (Archbishop) on Oct 27, 2003 at 22:55 UTC

    You exit before line 6 is reached - it is executed at runtime. You will get the behavior I think you expect by rearranging,

    use strict; { my $var = 1; sub test { print "var was '$var'\n"; $var++; print "var is now '$var'\n"; } } test(); exit 0;
    The extra braces are just to limit the scope of $var further. Search closure here for much more.

    After Compline,
    Zaxo

    the monastery: (36)
Re: Variable scoping outside subs
by QM (Parson) on Oct 27, 2003 at 22:52 UTC
    Line 6 is never evaluated. Execution ends at line 4.

    If you want line 6 to execute before test is called, place it before the call to &test().

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

      You're missing my point, comment it out and see what happens.
        I think you are likely confused by the fact that $var seems to be legally declared before the subroutine, but the subroutine is called before $var is declared. And yet, in the subroutine, $var has not been initialized to 1.

        In this case, the entire script is parsed and the my declaration is noted at compile time. The initialization of $var happens at runtime, except in your case you've exited before that point.

        I agree the behavior is strange. It seems that Perl is processing the declaration at compile-time, which is why the variable exists when the sub is declared, but isn't processing the assignment until that line is reached in runtime, which never happens.

        Perhaps you've touched on an area where the right thing to do is ambiguous, so Perl's idea of what to do differs from yours.

        Several people have already shown you ways to make this work, by making sure the my statement precedes the call to test in both compile- and run-time.

        my has both compile-time and run-time effects. See Abigail's comment in Unusual Closure Behaviour, though that's not directly related to your problem. [and damn those spelling variations]

        When the my line is commented out, I suspect that Perl is not complaining about the missing declaration because of the compile-time effect, even though the line is never executed.

        -QM
        --
        Quantum Mechanics: The dreams stuff is made of

Re: Variable scoping outside subs
by leriksen (Curate) on Oct 29, 2003 at 00:26 UTC
    One additional clue to the compile-time/run-time distinction is if you turn on -w in the she-bang line (or 'use warnings'). You get a warning from inside test()  Use of uninitialized value in concatenation (.) or string at ./script.pl line 13 So Perl knows about $var, but it is undefined - so the '= 1' part hasn't happened when we are in test()...
    +++++++++++++++++
    leriksen - Chief Cat Herder
    /#!/usr/bin/perl
    use warnings;use strict;use brain;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://302556]
Approved by PERLscienceman
Front-paged by thelenm
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (4)
As of 2023-01-27 14:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?