I accidentally stumbled over an interesting ability/quirk in Perl: a subroutine / function / method name may contain spaces. Since I couldn’t find any info about it in the perlsub man page or on Google I decided to write it down.
It should be obvious that you can’t create such a subroutine by defining it the traditional way, but in case it isn’t: you can’t. Perl will consider the first word to be your subroutine identifier, and the following word(s) to be invalid keywords.
use strict;
sub name With Spaces
{
print "works!\n"; # liar, doesn't work
}
Illegal declaration of subroutine main::name line 4.
NOTE: the following examples were tested in Perl versions 5.8.8 (circa 2006), 5.14.2 (circa 2011), and 5.28.2 (circa 2019).
To create a method name with a space, you have to manipulate the symbol table directly. (Indeed, I figured it out by accident thanks to an AUTOLOAD
ed method that did that.)
sub AUTOLOAD
{
my $self = shift;
( my $method = $AUTOLOAD ) =~ s{.*::}{};
if ( exists $self->{_attr}->{ $method } ) {
my $accessor = sub { return shift->{_attr}->{ $method } };
{
no strict 'refs';
*$AUTOLOAD = $accessor;
}
unshift @_ => $self;
goto &$AUTOLOAD;
}
return;
}
Stated more simply:
my $name = "name With Space";
*$name = sub { "works!" }; # insert directly to symbol table
Utilities like Test::Deep
“just work” if there’s a space:
cmp_methods( $obj,
[ 'name With Space' => 'value' ], # not a peep!
'Basic methods'
);
ok 1 - Basic Methods
The obvious question, though, is how to access it directly?
You can access a method using a variable, which is a pretty common thing to do on it’s own. (In my experience, anyway, YMMV).
my $name = 'name With Space';
my $value = $obj->$name; # works!
You can also create a reference to a string and immediately deference it.
my $value = $obj->${ \'name With Space' }; # works!
The second example works with regular function calls as well. Here’s a stand-alone example:
use strict;
{
no strict "refs";
my $name = "name With Space";
*$name = sub { "works!" };
}
print ${ \"name With Space" }, "\n";' # prints "works!"
I can’t recommend creating subroutines with spaces in the name as good style, but it’s helpful to know that it can happen and how to work with it when it does.