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

I read all of the replies to the node What can we assume in a BEGIN block ? and although I think I understand them, it does not help me understand the following:

Why, when compiling this code, using strict, does the compiler complain about the symbols not being defined?

#!/usr/bin/perl use strict; print keys %aa,", $bb, @cc\n"; BEGIN {our %aa=(1,11); our $bb=22; our @cc=(3,33); }
I thought that everything in the BEGIN block is parsed first, hence the symbol names (aa, bb and cc) are defined, before the rest of the code is parsed. Obviously I am wrong. Yet it also seems to work if strict is turned off.

Output with use strict

Global symbol "%aa" requires explicit package name at test.pl line 3. Global symbol "$bb" requires explicit package name at test.pl line 3. Global symbol "@cc" requires explicit package name at test.pl line 3. BEGIN not safe after errors--compilation aborted at test.pl line 8.
Output with # use strict (no strict)
1, 22, 3 33

I am confused...

Sandy

Replies are listed 'Best First'.
Re: Confusion about BEGIN block
by davido (Cardinal) on Oct 15, 2004 at 15:44 UTC

    Your confusion is with respect to the scoping that our creates. ...yes, scoping, believe it or not. See the documentation for our.

    our is indeed creating those symbols, and they live for the duration of the script (unless you delete them from the package-global symbol table). But for the purposes of strictures, their access is limited to the enclosing block in which they were created. You could still get at them outside the BEGIN block by using their fully qualified name (ie, $main::bb), since this doesn't raise the red flag for strictures, or declare them within the BEGIN block via the "use vars" pragma instead of our.


    Dave

      thankyou, I had forgotten about the scoping of "our".

      From the Camel Book, it says "In this respect (referring to the scoping of our) it differs from  use vars, which affects the entire package and is not lexically scoped.

      I tried

      #!/usr/bin/perl use strict; print keys %aa,", $bb, @cc\n"; BEGIN {use vars qw(%aa $bb @cc); %aa=(1,11); $bb=22; @cc=(3,33); }
      and it still complained. Is this because use strict truly insists on the our and/or my for all symbols?

      Sandy

        The strict check has a compiletime component to it. BEGIN blocks are guaranteed to be executed prior to anything else in the script, but they are compiled alongside the rest of the script, as they are seen, as the script is compiled line by line. In your script, 'print' is seen and compiled before your BEGIN block, and though the BEGIN block is executed before your 'print', the deed is already done; the compiler with strictures turned on has noticed that you have referred to a variable that hasn't been seen/declared yet. Yes, BEGIN executes first, but it is seen by the compiler in sequential order as the script is parsed top to bottom.

        Using the use vars pragma as I suggested solves your scoping issues, but it doesn't change the fact that at compiletime, with the use strict pragma in place, the compiler is seeing a variable getting used before it is seeing the variable being declared. That's where you need to change your order, or move the "our" to outside the BEGIN block.


        Dave

        You can use:
        #!/usr/bin/perl use strict; our (%aa, $bb, @cc ); print keys %aa,", $bb, @cc\n"; BEGIN {%aa=(1,11); $bb=22; @cc=(3,33); }
        Boris
Re: Confusion about BEGIN block
by pizza_milkshake (Monk) on Oct 15, 2004 at 15:52 UTC
    i am not completely certain i understand BEGIN{} blocks either, but let's take a look.
    #!perl -l use strict; use warnings; print "v: $v"; BEGIN{ my $v }
    gets us:
    Global symbol "$v" requires explicit package name at begin.pl line 4.
    BEGIN not safe after errors--compilation aborted at begin.pl line 5.
    
    ok, it's barfing on the print line. looks like the BEGIN declaration isn't getting handled first, or if it is, it's not the right scope. let's try swapping the order of the lines to see if it makes a difference
    #!perl -l use strict; use warnings; BEGIN{ my $v } print "v: $v";
    gets us
    Global symbol "$v" requires explicit package name at begin.pl line 5.
    Execution of begin.pl aborted due to compilation errors.
    
    different error. looks like our declaration isn't in scope. let's using "our" as you originally did, and try fully qualifying $v as $::v
    #!perl -l use strict; use warnings; BEGIN{ our $v } print "v: $::v";
    gets us
    Use of uninitialized value in concatenation (.) or string at begin.pl line 5.
    v: 
    
    ok, so that works, it just doesn't like the fact that $::v isn't defined. now let's try defining $v outside the begin block
    #!perl -l use strict; use warnings; my $v = 5; BEGIN{ $v++ } print "v: $v";
    gets us
    v: 5
    
    ok, so there's still no problems with scope, but does the BEGIN code even get run? let's try something else
    #!perl -l use strict; use warnings; my $v; BEGIN{ $v++ } print "v: $v";
    gets us
    v: 1
    
    so the BEGIN code is getting run... it looks like BEGIN requires a definition and will use it even if it's in non-BEGIN code, but it gets first dibs on assignment. the reason we got v: 5 is because the assignment = 5 happened AFTER the begin block, even though the declaration and assignment happened in the same statement

    fun stuff!

    perl -e"\$_=qq/nwdd\x7F^n\x7Flm{{llql0}qs\x14/;s/./chr(ord$&^30)/ge;print"

      I think I'm starting to get the hang of this, thankyou.

      I guess my greatest confusion was where and when symbol names needed to be declared. Mmmm.

      I think I understand that

      1. our $var is lexically scoped, and is just an alias for $__PACKAGE::var

      2. because our $var is lexically scoped, when it is in a BEGIN block, it does not mean that the alias (i.e. $var) is available outside of the BEGIN block.

      3. however, if the alias is defined outside the BEGIN block, it will be available for use within the BEGIN block because of the lexical scope if and only if it is declared before the BEGIN block is written

      EXAMPLE #1 works, #2 does not

      #!/usr/bin/perl use strict; our %aa; our $bb; our @cc; print keys %aa,", $bb, @cc\n"; BEGIN {%aa=(1,11); $bb=22; @cc=(3,33); }
      #!/usr/bin/perl use strict; BEGIN {%aa=(1,11); $bb=22; @cc=(3,33); } our %aa; our $bb; our @cc; print keys %aa,", $bb, @cc\n";
      4. all code within the BEGIN block is executed first, regardless of it's location within the file

      5. egads! no wonder I'm confused

        egads! no wonder I'm confused

        It seems you are overthinking this a great deal. The reason strict complains is simple -- it scans the code before anything is executed, and looks for proper scoping. If there is a variable used before it is scoped, strict complains. End of story. Strict does not unravel BEGIN blocks and figure out what will be executed when, it simply looks at the "physical" location of things in the code.

Re: Confusion about BEGIN block
by ysth (Canon) on Oct 15, 2004 at 16:44 UTC
    I thought that everything in the BEGIN block is parsed first
    No, there's nothing special about when BEGIN blocks are parsed/compiled. The difference is that they are run immediately after, whereas code not in a special block is run only once the entire unit is finished compiling.

    In some of the examples in this thread, there is an additional oddity that BEGIN blocks expose. Normally lexical variables are only available while in scope; that is, they are treated as if they are created when the scope is entered (at runtime) and destroyed when the scope is left. The exception is that prior to entering the scope for the first time, they already exist, and can be modified in a BEGIN block. Someday dave_the_m will document this :)