UNIVERSAL::can($don't, $do_this);

Ovid on 2006-01-24T21:54:36

I get awfully tired of writing this:

use Scalar::Util 'blessed';

if ( blessed($thing) && $thing->can('do_something') ) { ... }

Which is why I'm awfully tempted to just write this almost throwaway code and put it on the CPAN. I probably won't, but I toy with the idea.

package Can;

use strict;
use warnings;
use Scalar::Util 'blessed';
use base 'Exporter';
@EXPORT = 'Can';

sub Can {
  my ($object, $method) = @_;
  return unless blessed $object;
  return $object->can($method);
}

1;

And in my code:

use Can; # use Can () if you don't want importing

if ( Can( $thing, 'do_something' ) ) { ... }

Depending upon the type of code you write, it's quite easy to get an unblessed reference instead of an object. I get tired of always having to use blessed from Scalar::Util, but this is a common enough practice (I hit a lot in exception handling, for example) that it seems like a "write it and forget it" module might be useful.

And for those who do UNIVERSAL::can($object, $method), don't. If the object overrides &can, you're in trouble. If you already know your objects don't override &can, then you're relying on implementation details that you shouldn't be relying on.


This is also an API problem

jk2addict on 2006-01-24T22:02:34

I admit it. I'm guilty of UNIVERSAL::can($object, $method). Been there. Done than. Changed them when I run across them in old code.

My real beef with UNIVERSAL::can($object, $method) is one of usibility. What couldn't that have just made that method DTRT and simply return false/0 if $object wasn't an object at all. After all, in a sub as simple as this, I just want a true/false. No need to throw errors. It either can, or can't, regardless os whether $objectis or isn't an object.

Re:This is also an API problem

chromatic on 2006-01-24T22:33:07

What if $object contains a class name?

Re:This is also an API problem

jk2addict on 2006-01-24T22:50:16

Then return true. It's not telling a lie.

$classname->foo and &classname::foo exists, and can be called. How they are supposed to be called is up to the module author and consumer, and not the concern of can.

Re:This is also an API problem

chromatic on 2006-01-25T03:11:38

I simply mean that there isn't a single use for UNIVERSAL::can that makes a replacement easy to write. UNIVERSAL::isa is even worse.

Simpler means

merlyn on 2006-01-24T23:24:29

I'm universally replacing
UNIVERSAL::can($object, 'method')
with
eval { $object->can('method') }
. It Just Works.

Re:Simpler means

Alias on 2006-01-25T01:16:06

Unless ->can throws an exception, in which case you get a false negative...

I really need to get around to adding _CAN to Params::Util :/

Re:Simpler means

merlyn on 2006-01-25T01:33:10

well, if it threw an exception, it probably can't do that then. {grin}