Perl v5.32 adds Paul Evans’s infix isa
operator—the “class instance operator”. 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.
I think there’s some indication that if this works out, we might get the same thing for can
and does
.
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.
0 Comments.