A colleague has asked me recently why the following ends in Global symbol "$lib" requires explicit package name:
#! /usr/bin/perl use warnings; use strict; use lib my $lib = '.'; print $lib;

My answer was that the code is equivalent to

BEGIN { require lib; 'lib'->import(my $lib = '.'); }

But I was kind of unhappy about it. Is it a feature that use creates a scope around its parameters, or was it just easier to implement it like that? Have you noticed the behaviour and what do you think about it?

($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Replies are listed 'Best First'.
Re: Scope in use
by haukex (Monsignor) on Sep 04, 2017 at 09:00 UTC

    I recall this behavior being mentioned in a thread here not too long ago, unfortunately I can't seem to find it at the moment. (Update: AM found it, thanks)

    The use docs do say that it is "exactly equivalent" to BEGIN { require Module; Module->import( LIST ); }, which is pretty explicit. So taking that into account the behavior makes sense.

    But from a user's standpoint, the implicit block is certainly surprising.

    My first thoughts on this are that I'm not sure if it would be possible to get rid of the extra scope though... whether something happens at compile time or runtime can make a huge difference if it has side effects (not necessarily variable declarations, but the LIST can be arbitrarily complex).

Re: Scope in use
by LanX (Bishop) on Sep 04, 2017 at 14:48 UTC
    You are mixing compile-time and run-time behavior. *

    The double nature of my is probably causing false expectations, but this works:

    C:\Windows\system32>perl use warnings; use strict; my $lib; # compile time file scope (declaration) use lib $lib="XXX"; # compile time use scope (assignment) print $lib; # run time file scope (output) __END__ XXX C:\Windows\system32>


    but if you want to totally confuse your colleague show him this working code ;-)

    use warnings; use strict; my $lib; print $lib; # prints XXX use lib $lib="XXX"; __END__ XXX

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    *) additional to the effect already described in use has it's own scope?

      You are mixing compile-time and run-time behavior.

      I respectfully disagree.

      my does have both compile time and run time effects. my always has a compile time effect to declare the presence of one or more variables. It also has a run time effect. Before a block of code is run, it is compiled, thus the variables are declared so the rest of the block can compile. When the block is run, the run time effect of my is applied.

      In this case, the compiler is complaining "Global symbol "$lib" requires explicit package name" which also means that there isn't a declaration in scope. This implies the my in the use statement is in a different scope.

      When you remember that use X LIST is equivalent to BEGIN { require X; X->import(LIST)' }, this makes sense.

      If you don't know that (or forget it), it can/will be a surprise.

      In the OP's example, the run time effect is not the problem.

      The following works because my does have an effect at compile time:

      my $lib; # $lib declared at file scope use lib $lib = '.'; # file-scope $lib accessed in "use scope" print $lib; # file-scope $lib accessed in file scope
Re: Scope in use
by Anonymous Monk on Sep 04, 2017 at 13:51 UTC

    I don't see how the current behavior could possibly be called "good." The scope of a my variable doesn't start until the end of the statement it appears in...

    >perl -wle 'print($x, (my $x = 42), $x);print "now $x"' Use of uninitialized value $x in print at -e line 1. Use of uninitialized value $x in print at -e line 1. 42 now 42

    ... so AFAICT there's no way to refer to a lexical created in a use statement at all.

    But why would you write
    use lib my $lib = '.';
    in the first place? Probably because this doesn't work:
    my $lib = '.';
    use lib $lib;
    This seems to be a common problem that people run into. Should we tell them to write this instead?
    my $lib = '.';
    unshift @INC, $lib;
    Or maybe this?
    BEGIN { our $lib = '.' }
    use lib our $lib;
    IDK, but IMO that's the real issue.

      The following also works, though is equally surprising that it is needed.

      my $lib; use lib $lib = '.';

      This works because variables declared at one scope are accessible from scopes inside the declaring scope.

      Unfortunately, there isn't a good way to change this. It can, and probably should be, documented in the page for use and, maybe, also in the page for the lib pragma.

      (Maybe a warning could be added when parsing use to remind people that my inside use has no effect and suggest alternate syntax.)

        I think it's enough to document it together with scoping in general - Wherever that happens - as edge case of the "till the end of the surrounding block" rule.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: Scope in use (get rid of it)
by tye (Sage) on Sep 07, 2017 at 19:58 UTC

    There have been quite a few times over the last decade where I would have made something very nice had use not had that implied scope around it. So much so, that I dreamed of implementing a non-scope version of BEGIN (but never did):

    my $lib = 'foo' BEGIN;

    So if somebody patches Perl to make use not have an implied scope, that would make me happy.

    - tye        

      I think BEGIN::Lift claims to do that, but it only extends to lifting subroutine calls. Depending on your actual use case, this might be enough already.

      Update: Also, Devel::BeginLift, which does the same according to BEGIN::Lift.

Re: Scope in use
by Anonymous Monk on Sep 12, 2017 at 14:30 UTC
    The BNF grammar for use is too-forgiving, as a matter of practicality, because of the many different things that this pragma can do. The description in perldoc lib specifies that the parameter should be a list, and that is exactly what it should be, even if the grammar does not produce a syntax-error when you deviate from it.