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.