This is a chapter in Perl New Features, a book from Perl School that you can buy on LeanPub or Amazon. Your support helps me to produce more content.
This feature is stable in v5.36
Perl v5.32 adds Paul Evans’s infix isa
operator—the “class instance operator” as an experimental feature. It still has some issues to work out which prevent its use at the moment, but it looks promising. It subverts how the UNIVERSAL::isa
does its job and breaks that in the process. As an experimental feature, that’s fine, but you shouldn’t use this until that’s worked out.
There’s no word on versions for can
or does
.
One of the delightful things to note about this is addition is that it is one of the features whose development took place almost entirely through a GitHub issue and pull request. GitHub is now the primary repository for the Perl code, and has been since October 2019. This is a feature that I’ll want to use right away in new production code.
The UNIVERSAL
class, from which all other classes ultimately inherit, provides the basic isa
method. Pronouce that IS-A, like “a cat is a mammal”. I think the best explanation is in the now-removed Perl Barnyard OO Tutorial (perlboot). It’s also how we explain object-orientation in Intermediate Perl. We showed isa
in Effective Perl Programming too.
One way to use it involves eval
so that a non-object can still return false instead of a method error. If it’s not an object it doesn’t inherit:
if( eval { $var->isa( $some_package ) } ) { ... do stuff ... }
Sometimes you see this in function form to avoid the error if $var
is not an object, but this subverts any overridden isa
methods. Sadly, I think I use this somewhere in Effective Perl Programming too:
if( UNIVERSAL::isa( $var, $some_package ) ) { ... do stuff ... }
This new infix feature removes some of the syntax and obviates the eval
by returning false if $var
is not an object:
use v5.31.7; use warnings; use feature qw(isa); no warnings qw(experimental::isa); if( $var isa $some_package ) { ... do stuff ... }
The lefthand side can be anything. If it’s not an object, it’s false. If it’s an object that inherits from the class name on the righthand side, it’s true:
use v5.31.7; use warnings; use feature qw(isa); no warnings qw(experimental::isa); package Parent { sub new { bless {}, $_[0] } } package Child { our @ISA = qw(Parent) } my $obj = Child->new; my @tests = ( [ 'string', 'Some::Class', 'Plain string' ], [ 0xDEADBEEF, "Some::Class", 'Plain number' ], [ undef, "Some::Class", 'undef' ], [ $obj, "Parent", 'obj of Parent' ], [ $obj, "Child", 'obj of Child' ], [ $obj, "UNIVERSAL", 'obj of UNIVERSAL' ], [ $obj, "Other::Class", 'not obj of Other::Class' ], [ [], "ARRAY", 'array ref' ], [ {}, "HASH", 'hash ref' ], ); foreach my $test ( @tests ) { my( $candidate, $class, $label ) = $test->@*; say $label if $candidate isa $class; } say "Loaded class, bareword, Parent" if $obj isa Parent; say "Loaded class, bareword, Child" if $obj isa Child; # Normally, a bareword class needs to be loaded. This ignores # that. say "Non-loaded class, bareword" if $obj isa Other::Class; say "Done!";
This is the entire output, warnings (none!) and all. The program goes all the way to the end without die-ing, even though I use a class name that I haven’t defined:
obj of Parent obj of Child obj of UNIVERSAL Loaded class, bareword, Parent Loaded class, bareword, Child Done!
You can see more examples in the test file, t/op/isa.t.
The Problem
Note: this is fixed in v5.34, but still broken in v5.32.1..
If you use the new infix isa
, it breaks the normal use of UNIVERSAL::isa
, even outside the scope that enabled it:
use v5.32; use warnings; package Thingy { } my $x = bless {}, 'Thingy'; eval { use experimental 'isa'; say "Inside infix: ", $x isa 'Thingy'; say "Inside ->isa: ", $x->isa('Thingy'); }; say "AT 1: $@"; say "Outside ->isa: ", eval { $x->isa('Thingy') }; say "AT 2: $@";
The output shows that the method call inside the same scope as the feature fails. It also shows that the method call outside the scope fails.
Inside infix: 1 AT 1: Can't locate object method "isa" via package "Thingy" at /Users/brian/Desktop/isa.pl line 11. Outside ->isa: AT 2: Can't locate object method "isa" via package "Thingy" at /Users/brian/Desktop/isa.pl line 15.
This appears to happen only within the same file (compilation unit), so a different module that uses it is safe. If you don’t mix both uses in the same file, you might be okay. However, anyone who works on that file needs to know not to mix them, which means they need to know which one you’ve chosen to use. That’s too high of a maintenance burden in most cases.
Things to Remember
UNIVERSAL::isa
subverts overriddenisa
methods.- Calling the
isa
blows up if the invocant is not an object. - The experimental infix
isa
solves these problems. - You can’t mix both in the same compilation unit.