Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Logic for importing and strict vars?

by haukex (Bishop)
on Feb 27, 2019 at 08:03 UTC ( #1230603=perlquestion: print w/replies, xml ) Need Help??

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

Hi everyone, I'm looking for some documentation on why the fourth of these examples is an error:

$ perl -wMstrict -e 'BEGIN { package blah; *::x=sub{} } x' $ perl -wMstrict -e 'BEGIN { *::x=sub{} } x' $ perl -wMstrict -e 'BEGIN { package blah; *::x=\$a } $x++' $ perl -wMstrict -e 'BEGIN { *::x=\$a } $x++' Variable "$x" is not imported at -e line 1. Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. Execution of -e aborted due to compilation errors.

The explanation for why is here, so I'm really just looking for something in the official Perl docs, but I haven't found it yet. Any help would be appreciated :-)

Update: Thanks very much for all the replies! :-)

Replies are listed 'Best First'.
Re: Logic for importing and strict vars?
by vr (Curate) on Feb 27, 2019 at 12:08 UTC
    why the fourth of these examples is an error

    I'd say it's in accordance with strict (emphasis mine):

    strict vars

    This generates a compile-time error if you access a variable that was neither explicitly declared (using any of my, our, state, or use vars ) nor fully qualified.

    (Edit: to re-phrase, maybe it's more clear -- was neither explicitly declared nor is fully qualified.)

    Thus, no surprise that next 2 lines fail:

    perl -wMstrict -e "$::x=0; $x++" # not OK perl -wMstrict -e "package foo; $foo::x=0; $x++" # not OK

    Next 2 lines are your examples -- it's strange 2nd (your third) doesn't fail:

    perl -wMstrict -e "BEGIN{*::x=\$a} $x++" # not OK (1) perl -wMstrict -e "BEGIN{package foo; *::x=\$a} $x++" # OK (2)

    It has something to do with BEGIN:

    perl -wMstrict -e "{package foo; *::x=\$a} $x++" # not OK

    the deparsed output is an insult:

    C:\>perl -MO=Deparse -wMstrict -e "{package foo; *::x=\$a} $x++" Variable "$x" is not imported at -e line 1. Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. -e had compilation errors. BEGIN { $^W = 1; } use strict; { package foo; *main::x = \$a; } $main::x++;

    But also with "main" package. Next 2 are symmetrical with examples (1) and (2), just with "bar" instead of "main", but both OK now:

    perl -wMstrict -e "BEGIN{package bar; *bar::x=\$a} package bar; $x++" + # not OK perl -wMstrict -e "BEGIN{package foo; *bar::x=\$a} package bar; $x++" + # OK

    Edit 2: Actually, replacing "main" with "bar" doesn't change anything (fixed one of the lines above).

      It has something to do with BEGIN

      I have found this information:

      BEGIN: A BEGIN code block is executed as soon as possible, that is, the moment it is completely defined, even before the rest of the containing file (or string) is parsed

      So I suspect that that means that in case the BEGIN is not there the parser will detect the variable $x before it is created. In case the BEGIN is there, it is guarantied to execute that block first, so before it sees $x (or in this case actually $main::x) and by that time it exists because the block has already been executed.

      edit: It even looks like that BEGIN turns off certain warnings for this reason:

      perl -MO=Deparse -wMstrict -e "{ package bla { *::x=\$a } }" Name "main::x" used only once: possible typo at -e line 1. ...
      perl -MO=Deparse -wMstrict -e "BEGIN { package bla { *::x=\$a } }" # No problem
        It has something to do with BEGIN
        In case the BEGIN is there, it is guarantied to execute that block first, so before it sees $x (or in this case actually $main::x) and by that time it exists because the block has already been executed.

        Yes, I concur - and an interesting point regarding the warnings. I assume they don't occur because otherwise they'd get triggered by the import mechanism regularly, but it'd be interesting to find any docs on that, too...

      it's strange 2nd (your third) doesn't fail ... Actually, replacing "main" with "bar" doesn't change anything (fixed one of the lines above).

      Yes, that's the logic that I was looking for "official" documentation on - the difference is, as tye said, "when the variable slot of a glob gets assigned to by code compiled into another package".

Re: Logic for importing and strict vars?
by Eily (Monsignor) on Feb 27, 2019 at 08:31 UTC

    To add a little strangeness; for your third proposition, on v5.10 Linux I get:

    Name "blah::a" used only once: possible typo at -e line 1.
    but I don't on v5.26 Windows :)

    All I can say is that with B::Deparse the package prefix seems to be removed,

    perl -MO=Deparse -wMstrict -e "BEGIN { *::x=\$a } $x++" ... BEGIN { $^W = 1; } use strict; sub BEGIN { *x = \$a; } $main::x++;
    The output is exactly the same if I add package main; before setting *::x. (Also it replaces the $x++ by $main::x++, which would actually be valid code...) This might just be an optimization gone wrong?

      Eily: All I can say is that with B::Deparse the package prefix seems to be removed,

      No, that is turned around reasoning. I would say: The package prefix is not added.

      The problem that is described here is not in the BEGIN block but the last $x++ statement which should be explicitly mentioned: $main::x++; because $x is not exported imported. The Parser translates to '$main::x' automatically because the package statement resides in the BEGIN block. When it is outside the BEGIN block, it doesn't do that. In none of the cases, the variable $x is exported imported.

      edit: As far as documentation concerned I believe it can be found under: package, paragraph 2, the promise that a package statement does not affect lexically-scoped variables

        The prefix was there, and then it's not. I'm not sure why I couldn't call that being removed.

        In none of the cases, the variable $x is imported
        Importing is just writing in the glob slot from another package, which is what Exporter does (and it doesn't do anything else, the only special thing is that it is called implicitly, but you can call it explicitly and still have it work). The part that I missed on first read was the "from another package". Which means the variable is imported in the third case (edit: which is why the third one works).

      on v5.10 Linux I get: Name "blah::a" used only once: possible typo at -e line 1. but I don't on v5.26 Windows :)

      That'd be this change in v5.20: $a and $b warnings exemption

      All I can say is that with B::Deparse the package prefix seems to be removed

      True, although that's with the error still happening, I'm not sure if the deparse can be trusted in that case. It looks a little different without strict vars:

      $ perl -MO=Deparse -wMstrict=refs,subs -e 'BEGIN { *::x=\$a } $x++' BEGIN { $^W = 1; } use strict 'refs', 'subs'; sub BEGIN { *x = \$a; } ++$x; -e syntax OK
Re: Logic for importing and strict vars?
by Eily (Monsignor) on Feb 27, 2019 at 13:18 UTC

    Subs are special, perl doesn't care so much if a function is declared to allow it to be called, this is valid:

    perl -cE "use strict; use warnings; myFunction()" -e syntax OK
    For the code to work you just need to have a function in the *::myFunction slot at execution time. So I guess this is why the second line works?

    Now for the last one, tye's post actually has the answer:

    when the variable slot of a glob gets assigned to by code compiled into another package
    For the import to be valid, the glob as to be written from another package.

      Subs are special, perl doesn't care so much if a function is declared to allow it to be called

      Yes, although the parens are significant in your example (which is why mine left them out):

      $ perl -wMstrict -e 'BEGIN { *::x=sub{} } x' $ perl -wMstrict -e 'BEGIN { *::x=sub{} } x()' $ perl -wMstrict -e ' { *::x=sub{} } x()' $ perl -wMstrict -e ' { *::x=sub{} } x' Bareword "x" not allowed while "strict subs" in use at -e line 1. Execution of -e aborted due to compilation errors. $ perl -wMstrict -e 'x' Bareword "x" not allowed while "strict subs" in use at -e line 1. Execution of -e aborted due to compilation errors. $ perl -wMstrict -e 'x()' Undefined subroutine &main::x called at -e line 1. $ perl -wMstrict -e '&x' Undefined subroutine &main::x called at -e line 1.
Re: Logic for importing and strict vars?
by LanX (Sage) on Feb 27, 2019 at 13:19 UTC
    I'm still sleepy and have no documentation...

    Here my guess:

    Strict doesn't complain about exported vars, they are "declared" implicitly.*

    Your last line is not an export but only a simple aliasing, since $x and $a belong to the same package.

    *) Most probably the same mechanism use vars is employing.

    Update

    The fact that $a is a special variable makes it even more complicated...

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      If that was the case BEGIN { package OfEmpire; *::x=\$::a } would have the same problem, but it's valid. What matters according to tye is the package in which the instruction that overwrites the glob is located.

        Maybe ...

        You are talking about the exact mechanism that defines "exporting" resp. "Importing".

        My statement holds the last line doesn't export.

        Furthermore:

        I can't see tye commenting here.

        And using $a is unfortunate.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      The fact that $a is a special variable makes it even more complicated...

      I just used it because I wouldn't have to declare it, but it's actually not significant to my example, sorry for the added confusion:

      $ perl -wMstrict -e 'BEGIN { package blah; *::x=\$::y } $x++' Name "main::y" used only once: possible typo at -e line 1. $ perl -wMstrict -e 'BEGIN { *::x=\$::y } $x++' Variable "$x" is not imported at -e line 1. Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. Execution of -e aborted due to compilation errors.

        While experimenting with this code I stepped away from using $a, main and one-liners and I can report to you that the results are exactly the same:

        package Foo ; use strict ; our $t = 2 ; { BEGIN { # package Bar ; # added *Foo::x = \$Foo::t; } ++$x ; # ++$Foo::x; # no errors print $x ; # print $Foo::x; # no errors }

        Small note regards the Deparse results: With 'package Bar' disabled it looks like *Foo::x = \$Foo::t; becomes: *x = \$t; and with 'package Bar' enabled it becomes: *Foo::x = \$Foo::t;. Inside the BEGIN statement I think it is likely that the first form is not a valid trigger to 'declare' $x and the latter form is valid, but the reason could also be what tye said regards: "when the variable slot of a glob gets assigned to by code compiled into another package". But I am still confused though 'what' declared means. Is it turned into a lexical scoped package variable, or is it similar to 'use vars'?. This line in 'use' documentation is not clear to me:"...which are effective through the end of the file..." in the text: "Some of these pseudo-modules import semantics into the current block scope (like strict or integer , unlike ordinary modules, which import symbols into the current package (which are effective through the end of the file)".

        In case you do wish to use this as a one-liner (Windows):

        perl -MO=Deparse -wMstrict -e "package Foo ; our $t = 2 ; { BEGIN { package Bar { *Foo::x=\$Foo::t } } ; ++$x ; print $x }"
Re: Logic for importing and strict vars?
by LanX (Sage) on Feb 27, 2019 at 17:26 UTC
    I don't think it is properly documented.

    (You might find a mention in perlapi or perlguts but that's not my definition of proper.)

    Probably the best place would be perlmod#Symbol-Tables which explains the *typeglob operator.

    Plus cross-references coming in from strict, import ...

    You could use perlbug to suggest patches to the docs.

    HTH! :)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      I don't think it is properly documented.

      Yes, I'm starting to think that too. At least we gave the question some more exposure here on PerlMonks :-)

Re: Logic for importing and strict vars?
by Anonymous Monk on Feb 27, 2019 at 10:54 UTC
    Why? UTSL is the only real docs for implementation details. Everybody else just uses vars.

      If you've found a good explanation in the source, please do share ;-P

        Why would i look since im happy to use vars?

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2021-09-29 03:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?