Search code examples
perllexical-scope

Does my $_; do anything if $_ is implied


I think the answer is yes but I just want to make sure. so if I have

sub something {
    my $_;
    my @array = ...;
    while ( @array ) {
        say;
    }
}

is the my $_; actually effective at lexicalizing the parameter passed to the say?

In this particular case I'm using the DZP::UnusedVarsTests and it's complaining that I haven't used my $_; and I suspect it's a bug since I'm using it in a case where it's implied.


Solution

  • The short answer is Yes. It makes the functions in that scope use the lexically scoped $_, and not the global $_. If they write back to $_, as in the case of s///, you will have some level of damage control.

    Per perldoc perldelta (5.10.0):

    "List::Util::first" misbehaves in the presence of a lexical $_ (typically introduced by "my $_" or implicitly by "given"). The variable which gets set for each iteration is the package variable $_, not the lexical $_ [RT #67694].

    A similar issue may occur in other modules that provide functions which take a block as their first argument, like

    foo { ... $_ ...} list
    

    And, in perldoc perl591delta it goes on to say:

    Lexical $_

    The default variable $_ can now be lexicalized, by declaring it like any other lexical variable, with a simple

         my $_;
    

    The operations that default on $_ will use the lexically-scoped version of $_ when it exists, instead of the global $_.

    In a "map" or a "grep" block, if $_ was previously my'ed, then the $_ inside the block is lexical as well (and scoped to the block).

    In a scope where $_ has been lexicalized, you can still have access to the global version of $_ by using $::_, or, more simply, by overriding the lexical declaration with "our $_".

    Examples

    I wanted to provide some examples of why this functionality would be used:

    my $_ = 'BOOM!';
    
    sub something {
        my $_;                         ## Try running with and without
        my @array = qw/foo bar baz/;
        while ( $_ = pop @array ) {
            say;
        }   
    }   
    
    something();
    
    say;
    

    And, another example

    my $_ = 'foo';
    
    sub something {
      my $_ = $_;  ## Try running with and without
      s/foo/bar/;
      $_;
    }
    
    something();
    
    say;