Turn off indirect object notation

Perl v5.32 adds a way to turn off a Perl feature that you shouldn’t use anyway. You can still use this feature, but now there’s a way to take it away from you. And, with the recent Perl 7 announcement, we see why. Eventually Perl wants to get rid of indirect object notation (and I explain that more in Preparing for Perl 7.

Indirect object notation is a way of putting the cart before the horse. The method name shows up in front of the invocant, whether a class name or an instance:

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = new Horse;
cart $horse;

C++ programmers (maybe Java) may look and that and think “Yeah, so”, and that’s fine. But Perl having two ways to do it is one too many. It feels weird to say that, but in the code that makes the perl binary doesn’t have to use the same philosophy it extends to user code. If the Perl core didn’t have to support this second way, its parsing could be simpler. And, cleaner internals makes it easier to support new ideas. This sort of clean up in the past several years helps people help perl.

Consider this problem that I encounter every week. I’ve forgotten to enable the say feature (wouldn’t it be nice if we could have nice things for free?), so I get a weird error:

use Mojo::URL;
my $url = Mojo::URL->new( 'https://www.example.com' );
say $url;

Because Perl supports indirect object notation, the runtime error thinks that say must be a method for the $url object:

Can't locate object method "say" via package "Mojo::URL"

Imagine a package that had an AUTOLOAD that would try to handle that say. That’s some weird voodoo.

I don’t think the indirect object notation was ever the dominant form, but it also wasn’t rare. Some popular modules used it in their docs. But, the direct object notation is modern practice. Perl Best Practices recommends against it in section 15.12 and the Perl::Critic policy Objects::ProhibitIndirectSyntax prohibit it. That’s 15 years ago!

You should use the direct object notation, which you’re already likely to see most of time:

my $horse = Horse->new;
$horse->cart;

This doesn’t help me avoid that mistake that I showed earlier. I wasn’t trying to use the indirect object and it still shows up. This is where the knobs and dials come in.

Perl v5.32 now has a way to turn off the indirect object syntax. Actually, it has a way to turn it on, but it’s turned on by default in v5.32, and is already available in all previous Perl 5 versions. This still works and outputs “Neigh!”:

use v5.32;

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = new Horse;
cart $horse;

Use the feature pragma to turn off indirect:

use v5.32;
no feature qw(indirect);

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = new Horse;
cart $horse;

The error shows that perl doesn’t understand the code and gets confused that the method name:

Bareword found where operator expected at ..., near "new Horse"
	(Do you need to predeclare new?)
Scalar found where operator expected at ..., near "cart $horse"
	(Do you need to predeclare cart?)
syntax error at..., near "new Horse"
Global symbol "$horse" requires explicit package name (did you forget to declare "my $horse"?) at ...

Change that to the direct object notation and it works again:

use v5.32;
no feature qw(indirect);

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = Horse->new;
$horse->cart;

There’s a way that you can do this before v5.32 too. The CPAN module indirect will warn about indirect object notation (but the method calls still work):

use v5.32;
no indirect;

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = new Horse;
cart $horse;

The warning is better than the parser confusion for v5.32, so you might want to start with this module before turning off all uses (or use Perl::Critic too):

Neigh!
Indirect call of method "new" on object "Horse" at ... line 10.
Indirect call of method "cart" on object "$horse" at ... line 11.

Make that an error by importing fatal:

use v5.32;
no indirect 'fatal';

package Horse {
	sub new  { bless {}, $_[0] }
	sub cart { print "Neigh!" }
	}

my $horse = new Horse;
cart $horse;

Now the program dies on the new:

Indirect call of method "new" on object "Horse" at ... line 9.

Indirect filehandles

Some of you may know that filehandles are objects but we generally treat them as functions. This is the reason there’s no comma between the filehandle and the first argument:

print STDOUT
print { $filehandle } “Hello world”;
$filehandle->print( “Hello world” );

So far, no feature qw(indirect) does not disturb these.

Conclusion

Here’s the thing for you to think about: there’s a way for you to turn off this feature, but it’s enabled by default. You don’t have to stop using indirect object notation. But now Perl has that knob and dial and eventually might disable it by default.

Leave a comment

0 Comments.

Leave a Reply


[ Ctrl + Enter ]