Calling UNIVERSAL::can() as a function is allowed. (was: [OT] best way to determine existence of a function in a package)

Andy Wardley abw at wardley.org
Wed Nov 21 19:17:07 GMT 2007


Matt S Trout wrote:
 > You should never, ever, EVER use any UNIVERSAL::* as a function. That's not
 > what it's for, not how it's designed to work and can seriously fuck things
 > up.

I'm sorry, but you're wrong.

Jonathan Rockway wrote:
 > Anyway, please read man UNIVERSAL and UNIVERSAL::isa (the CPAN version)
 > for more information.

$ perldoc UNIVERSAL
[snipola]

   "can" can be called as a class (static) method, an object method,
   or a function.

   When used as a function, if "VAL" is a blessed reference or package
   name which has a method called "METHOD", "can" returns a reference
   to the subroutine.  If "VAL" is not a blessed reference, or if it
   does not have a method "METHOD", undef is returned.

In my humble opinion, that's the end of it right there.  It says in the
official documentation that UNIVERSAL::can *can* be called as a function.
That *is* what it's for, it *is* how it's designed to work, and yes, if
you're not careful, it can seriously fuck things up.  But hey, so can
"rm -fr /", so use it wisely.

The explanations that Matt, Jonathan, chromatic and others give are entirely
valid for the subset of Perl programs that are striving towards a strict
interpretation of the Object Oriented paradigm.

But as well all know, Perl isn't a strict OO language.  Rather, it prides
itself on the fact that it can be a procedural language, a functional
language, an object oriented language, or some combination of those and
more.

So as far as I'm concerned:

<fact>
   It is officially OK to call UNIVERSAL::can as a function...
</fact>

<corollary>
  ...but it's (usually) Not a Good Thing.
</corollary>

I've been personally bitten by the OO dogma when I started getting bug
reports about TT not working with one of the popular database ORM packages
(Class::DBI?  DBIx::Class?  Can't remember off hand).  It turns out that
this package had recently started using the UNIVERSAL::can from CPAN which
advertises itself as a "Hack around people calling UNIVERSAL::can() as a
function".

Now although I'm pretty much an OO person myself and would normally follow
best OO practice by not calling U::can as a function, I can't account for
the plethora of other Perl modules that people want to use with TT and have
"just work".  So with good reason and careful consideration, I use U::can
as a function in one particular part of the TT variable lookup code.  I do
so because it's the Right Thing[tm] to do and the official documentation
backs me up on that.

Unfortunately the act of loading the UNIVERSAL::can module let slip the
OO dogmas of war on not only TT, but also on every other module that Jane
Random User happened to be using to build her web site.  Needless to say,
things broke and a hoard of angry badgers descended on the TT helpdesk
wielding large sticks with nails and other nasty bits sticking out the end.
Luckily for me, they saw the funny side when I explained the mixup and we all
had a laugh about it.  But I don't think they'll be so understanding next
time.

So count me among those that think that deliberately that breaking a perfectly
valid and officially sanctioned feature of Perl is Plain Wrong.

[ And for the record, it's also entirely correct and valid to use 'goto' in a
   Perl program.  Heck, you can even run with scissors if you really want to
   live dangerously.  I'm not going to stop you. ]

Of course, I wouldn't usually recommend these kind of activities unless you
know exactly what you're doing and understand why they're usually frowned upon
(or have a certificate showing successful completion of a training course in
"Personal Safety While Running With Scissors and Other Dangerous Household
Objects"). But I generally don't subscribe to the "never, ever, EVER" school
of programming (or extreme scissor-sports). I would rather have my Perl
programs work in the way that the docs say they will, than how the author of
another module written a long time ago in a galaxy far, far away thinks they
should work.

(No offence intended to chromatic or anyone else, in case it comes across
that way - I have no objection to anyone writing a module that monkeys around
with Perl in whatever way they see fit.  Just as long as we can agree on what
an un-monkeyed version of Perl looks like, and that includes calling
UNIVERSAL::can() as a function 'cos Larry says so and rule #1 applies)

A




More information about the london.pm mailing list