Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undocumented Perl variable %_?

Tags:

perl

I recently discovered what seems to be an undocumented variable in Perl, %_. I don't remember exactly how I stumbled across it (it was last week), but I had a typo in my code where I was using map and instead of $_->{key} I used $_{key}. When I found the mistake, I was surprised that it didn't generate an error, and I verified that use strict and use warnings were in place.

So, I did up a small test, and sure enough it runs without any warnings or errors:

$ perl
use strict;
use warnings;
print keys %_;
$

So, all I can figure is that %_ is defined somewhere. I can't find it in perlvar, so what's the deal? It doesn't have any contents in the script above.

like image 565
Chad Avatar asked Apr 19 '16 16:04

Chad


3 Answers

Punctuation variables are exempt from strict. That's why you don't have to use something like our $_; before using $_. From perlvar,

Perl identifiers that begin with digits, control characters, or punctuation characters [...] are also exempt from strict 'vars' errors.


%_ isn't undocumented. From perlvar,

Perl variable names may also be a sequence of digits or a single punctuation or control character (with the literal control character form deprecated). These names are all reserved for special uses by Perl

You can have a hash named _ because _ is a valid name for a variable. (I'm sure you are familiar with $_ and @_.)

No Perl builtin currently sets it or reads %_ implicitly, but punctuation variables such as %_ are reserved.


Note that punctuation variables are also special in that they are "super globals". This means that unqualified %_ refers to %_ in the root package, not %_ in the current package.

$ perl -E'
   %::x    = ( "%::x"    => 1 );
   %::_    = ( "%::_"    => 1 );
   %Foo::x = ( "%Foo::x" => 1 );
   %Foo::_ = ( "%Foo::_" => 1 );

   package Foo;
   say "%x      = ", keys(%x);
   say "%_      = ", keys(%_);
   say "%::x    = ", keys(%::x);
   say "%::_    = ", keys(%::_);
   say "%Foo::x = ", keys(%Foo::x);
   say "%Foo::_ = ", keys(%Foo::_);
'
%x      = %Foo::x
%_      = %::_      <-- surprise!
%::x    = %::x
%::_    = %::_
%Foo::x = %Foo::x
%Foo::_ = %Foo::_

This means that forgetting to use local %_ (as you did) can have very far-reaching effects.

like image 114
ikegami Avatar answered Nov 02 '22 08:11

ikegami


It's not undocumented, it's just unused. You'll find it's always empty

perldoc perlvar says this

Perl variable names may also be a sequence of digits or a single punctuation or control character ... These names are all reserved for special uses by Perl; for example, the all-digits names are used to hold data captured by backreferences after a regular expression match.

So %_ is reserved but unused.

Hash variables are the least common, so you will find that you can use %1, %( etc. as well (code like $({xx} = 99 is fine) but you will get no warning because of backward-compatability issues

Valid general-purpose variable names must start with a letter (with the utf8 pragma in place that may be any character with the Unicode letter property) or an ASCII underscore, when it must be followed by at least one other character

like image 45
Borodin Avatar answered Nov 02 '22 08:11

Borodin


$_ is a global variable. Global variables live in symbol tables, and the built-in punctuation variables all live in the symbol table for package main.

You can see the contents of the symbol table for main like this:

$ perl -MData::Dumper -e'print Dumper \%main::' # or \%:: for short
$VAR1 = { 
          '/' => *{'::/'},
          ',' => *{'::,'},
          '"' => *{'::"'},
          '_' => *::_,
          # and so on
        };

All of the above entries are typeglobs, indicated by the * sigil. A typeglob is like a container with slots for all of the different Perl types (e.g. SCALAR, ARRAY, HASH, CODE).

A typeglob allows you to use different variables with the same identifier (the name after the sigil):

${ *main::foo{SCALAR} } # long way of writing $main::foo
@{ *main::foo{ARRAY} }  # long way of writing @main::foo
%{ *main::foo{HASH} }   # long way of writing %main::foo

The values of $_, @_, and %_ are all stored in the main symbol table entry with key _. When you access %_, you're actually accessing the HASH slot in the *main::_ typeglob (*::_ for short).

strict 'vars' will normally complain if you try to access a global variable without the fully-qualified name, but punctuation variables are exempt:

use strict;

*main::foo = \'bar'; # assign 'bar' to SCALAR slot
print $main::foo;    # prints 'bar'
print $foo;          # error: Variable "$foo" is not imported
                     #        Global symbol "$foo" requires explicit package name
print %_;            # no error
like image 26
ThisSuitIsBlackNot Avatar answered Nov 02 '22 08:11

ThisSuitIsBlackNot