Search code examples
perlscopinglexical-scope

Variables declared with our and BEGIN block interactions


Why would an uninitialized variable behave/interact differently than an initialized variable in this situation:

use strict;
use warnings;

our($foo) = 0;
BEGIN {
        $foo = 2;
}

our($bar);
BEGIN {
        $bar = 3;
}

print "FOO: <$foo>\n";
print "BAR: <$bar>\n";

results in:

$ perl test.pl
FOO: <0>
BAR: <3>

Perl version:

$ perl -v

This is perl 5, version 22, subversion 0 (v5.22.0) built for x86_64-linux

Solution

  • First of all, it's not an initializer; it's a plain old assignment. It happens when the code is executed, not when the variable is created.

    Secondly, BEGIN blocks are evaluated as soon as the block is compiled.

    As such, what you wrote is basically equivalent to the following:

    # Compile phase
    use strict;
    use warnings;    
    our $foo;
    $foo = 2;
    our $bar;
    $bar = 3;
    
    # Runtime phase
    ($foo) = 0;
    ($bar);
    print "FOO: <$foo>\n";
    print "BAR: <$bar>\n";
    

    More precisely,

    1. Compile phase
      1. Compile use strict;.
      2. Execute require strict;.
      3. Execute import strict;.
      4. Compile use warnings;.
      5. Execute require warnings;.
      6. Execute import warnings;.
      7. Compile our($foo) = 0;. (Only creates $foo.)
      8. Compile BEGIN block:
        1. Compile $foo = 2;.
      9. Execute BEGIN block:
        1. Execute $foo = 2;.
      10. Compile our($bar);. (Only creates $bar.)
      11. Compile BEGIN block:
        1. Compile $bar = 3;.
      12. Execute BEGIN block:
        1. Execute $bar = 3;.
      13. Compile print "FOO: <$foo>\n";.
      14. Compile print "BAR: <$bar>\n";.
    2. Runtime phase
      1. Execute ($foo) = 0;.
      2. Execute ($bar);.
      3. Execute print "FOO: <$foo>\n";
      4. Execute print "BAR: <$bar>\n";

    As you can see,

    • You assign 2 to $foo in 1.9.1, then you assign 0 to $foo in 2.1.
    • You assign 3 to $bar in 1.12.1.