7

I want to check if a $thing is an object blessed as a package (e.g. __PACKAGE__). One idea is:

use Scalar::Util qw(blessed);

defined blessed $thing && blessed $thing eq __PACKAGE__

Is there a better and/or more elegant way that avoids checking if the return value of blessed is defined?

Another approach is (blessed $thing or '') eq __PACKAGE__, but I'm not sure if a package can legally be empty or not.

Also, based on Perl Monks, UNIVERSAL::isa($thing, __PACKAGE__) is another way, but that approach is permissive of more things.

Moh
  • 304
  • 2
  • 8
  • 1
    Sidenote: Don't use `UNIVERSAL::isa` as a function. Instead: `blessed $thing && $thing->isa($class)` or `eval { $thing->isa($class) }` – amon Feb 24 '14 at 08:42
  • @amon: Ahh, the latter of those looks very elegant (and even recommended from [perldoc universal](http://perldoc.perl.org/UNIVERSAL.html) even though it seems less performant). Is the main advantage of doing `$thing->isa($class)` because the class of `$thing` may override the `isa` method and that we should trust its implementation? – Moh Feb 24 '14 at 09:52
  • 1
    @Monith yes, exactly. This kind of flexibility is important when trying to extend Perl's OOP model. The same holds for the more general [`DOES` method](https://metacpan.org/pod/UNIVERSAL), which encodes an “*is somehow compatible with*”-relation: e.g. mocking instead of inheritance. – amon Feb 24 '14 at 10:10
  • 2
    See also [Safe::Isa](https://metacpan.org/pod/Safe::Isa). – tobyink Feb 24 '14 at 14:13
  • @amon: I looked into DOES, and the idea of roles seems really awesome. However, I don't see the benefit of roles over abstract classes: I can't imagine how it's possible (as of now) to implement [the scenario described here](http://www.oreillynet.com/onlamp/blog/2006/08/roles_composable_units_of_obje.html) where a package does two roles (e.g. Doglike & Treelike) that specify the same name of behavior (e.g. bark) but with different semantics. Seem like Perl 6 also has no solution yet after reading the wiki and [this article](http://modernperlbooks.com/mt/2009/04/the-why-of-perl-roles.html). – Moh Feb 25 '14 at 09:08
  • 1
    @tobyink: That's a really neat module. I'd love it if I could conveniently use my own subroutine names like `$does` instead of `$_DOES` upon using that module, e.g. via `use Safe::Isa DOES => q($does);`. Maybe that could be a user contribution :). – Moh Feb 25 '14 at 09:15
  • @Mohith From that article: “*The role resolver must detect conflicts in role method names and require disambiguation. When you try to define a `DogwoodTree` that does both `Doglike` and `Treelike`, you must disambiguate explicitly, whether composing in one method or the other or providing your own `bark()` method that redispatches […] depending on context*”. Some languages like C# allow you to implement a method only for a specific interface, thus avoiding naming conflicts. Roles are important for mocking (e.g. when testing), beyond that they're not strictly *needed* with multiple-inheritance. – amon Feb 25 '14 at 09:29
  • @Mohith: `require Safe::Isa; my $does = $Safe::Isa::_does;` – tobyink Feb 25 '14 at 10:28

2 Answers2

11

You can use the predefined ref function:

ref($thing) eq __PACKAGE__

That said, I think the more-permissive isa is really better practice. You shouldn't generally need to check if an object's type is exactly something.

[…] I'm not sure if a package can legally be empty or not.

It cannot. (And incidentally, if you try to bless a reference to '', it will actually get blessed into main. Perl will warn you about this, provided you have -w or use warnings.)

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • The concern I have with `ref` is that it acts differently than `blessed`. For example, `ref {} eq 'HASH'` vs. `ref (bless [], 'HASH') eq 'HASH'`. (even though it seems like it's bad practice to bless something as a builtin type, and even though it seems like bad practice to check if a type has been blessed as a builtin type). – Moh Feb 24 '14 at 06:50
  • @MohithMuddasani: I see what you mean, but it could only come up if you're actually inside a package named `'HASH'`, so if you're inside such a package, you'll just have to know not to use this. (Actually, I don't think I've *ever* used `__PACKAGE__`. What is it useful for? Inside files loaded with `do`?) – ruakh Feb 24 '14 at 07:00
  • That's true, I don't think I'll be writing a package named `'HASH'` anytime soon :). I have a few use-cases for `__PACKAGE__`, such as making sure an internal subroutine isn't called from outside the package: `croak 'Internal method called externally' if caller ne __PACKAGE__;`. (Though I wonder if it's better to use an anonymous subroutine assigned to a lexical variable in package scope -- just to allow a child class to use the subroutine name for different purposes) – Moh Feb 24 '14 at 07:47
  • @Mohith: What I mean is, why `caller ne __PACKAGE__` rather than `caller ne 'Foo'`? How often do you write code without knowing what package it's in? – ruakh Feb 24 '14 at 16:40
  • 1
    I think the main advantage of using `__PACKAGE__` instead of `Foo` directly is just in case I'd like to refactor the name of the package. That way, I can just update the file path `Foo.pm` and package declaration `package Foo;` and avoid worrying about where I hard-coded `Foo`. I guess it's only advantageous during development, since once it's being distributed it probably shouldn't change. – Moh Feb 24 '14 at 23:42
4

Use the Safe::Isa module from CPAN:

$possible_object->$_isa('DateTime')
Daniel Böhmer
  • 14,463
  • 5
  • 36
  • 46
Alexander Hartmaier
  • 2,178
  • 12
  • 21
  • Note that this returns true also if the object inherits the named package. That is what I wanted though. For this case you can also simply use `UNIVERSAL::isa($possible_object, 'DateTime')` – Daniel Böhmer Oct 27 '16 at 07:30