Sunday, December 2, 2012

How to test if something is a Perl class ?

For Data::Domain I needed a way to test if something is a Perl class. Since UNIVERSAL is the mother of all classes, it seemed to make sense to define the test as

defined($_) && !ref($_) && $_->isa('UNIVERSAL')

Other people did it through $_->can('can') or UNIVERSAL::can($_, 'can'). Both ways used to work fine, but this is no longer true since June 22, 2012 (bleadperl commit 68b4061) : now just any string matches these conditions.

At first I found this change a bit odd, but somehow it makes sense because any string will answer to the 'can' and 'isa' methods. Also, bless({}, 'Foo') and 'Foo' now return consistent answers for ->isa() and for ->can(), which was not the case before. So let's agree that this change was a good step.


But now it means that  while every object or class is a UNIVERSAL, the reverse is not true : things that are UNIVERSAL are not necessarily objects or classes. Hum ... but what "is a class", really ?

Moose type 'ClassName' defines this through Class::Load::is_class_loaded, which returns true if this is a package with a $VERSION, with an @ISA, or with at least one method. By this definition, an empty package is not a class. However, perldoc says that a class is "just a package", with no restriction.

So after some thoughts I ended up with this implementation :

defined($_) && !ref($_) && $_->can($_)

This returns true for any defined package, false otherwise, and works both before and after June 22.

Thoughts ?




6 comments:

  1. Hello openid person... the problem with this is that it evaluates to true for package "Foo" if only package "Foo::Bar" is defined.

    ReplyDelete
  2. Replies
    1. > Use Scalar::Util::blessed
      Well, no, this tells if something is an _object_, but not if a given string is a _class_

      Delete