21

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chad
  • 693
  • 5
  • 17

3 Answers3

22

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.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    http://perldoc.perl.org/perlvar.html#The-Syntax-of-Variable-Names addresses this more clearly, at least with regard to exemption from strict and the list of alphabetic identifiers that are classed with the punctuation one. – ysth Apr 19 '16 at 16:50
  • @ysth, I disagree. I believe "*[variables such as `%_`] are exempt from the effects of the package declaration and are always forced to be in package main*" is not nearly as clear as "*unqualified `%_` refers to `%_` in the root package, not `%_` in the current package*". I didn't mention strict exemption, though. – ikegami Apr 19 '16 at 16:55
  • 2
    It's lame that Perl doesn't give any sort of warning when you use `%_`: it's reserved to Perl, but Perl doesn't assign it any meaning, so any use of it is almost certainly a mistake of some sort. – ruakh Apr 20 '16 at 03:19
  • 1
    @ruakh, Easy to fix yourself: Write a module that adds magic which croaks when activated to variables you deem dangerous. Alternatively, write a `perlcritic` rule and lint your code using it. – ikegami Apr 20 '16 at 04:00
  • 1
    @ikegami: I hope you're joking? I mean, if we removed each Perl feature that could easily be supplied by users, Perl would be a very poor language indeed. – ruakh Apr 20 '16 at 04:09
  • 1
    @ruakh, wut. Noone said anything about removing any features. We were talking about a behaviour you want added. Well, there's nothing stopping you; I provided two easy solution that might even already exist. – ikegami Apr 20 '16 at 04:22
  • 1
    @ikegami: No one said anything about removing any features, but your argument is that they should never have been added, i.e., that Perl should suck. As someone who happens to feel that Perl should *not* suck, I think it would be better if Perl provided appropriate warnings. (Besides, your solutions are clever, but neither would really help. I'm not worried that I might use `%_`, since I've read your answer now. I'm worried about someone like me but who hasn't read it, and I'm worried about something like `%_` but that I don't know about yet.) – ruakh Apr 20 '16 at 05:24
  • @ruakh, I never said anything of the kind!!! I didn't even make any arguments on the subject. – ikegami Apr 20 '16 at 14:41
15

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

Borodin
  • 126,100
  • 9
  • 70
  • 144
6

$_ 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
ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110