Chad Chad - 6 months ago 10
Perl Question

Undocumented Perl variable %_?

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
and instead of
I used
. When I found the mistake, I was surprised that it didn't generate an error, and I verified that
use strict
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
, so what's the deal? It doesn't have any contents in the script above.


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.