Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

I don't understand UNIVERSAL::DOES()

by rlb3 (Deacon)
on Mar 08, 2007 at 15:58 UTC ( #603840=perlquestion: print w/replies, xml ) Need Help??
rlb3 has asked for the wisdom of the Perl Monks concerning the following question:


I've been toying around with perl 5.9.4. Just testing out some of the new features. One of those feature I don't understand is UNIVERSAL::DOES(). I've read the perldelta for perl 5.9.4 and the newest edition of The Perl Review which a has an article on perl 5.9.4, but I'm still not getting it. I don't see how it is difference from ISA. Here is some test code I wrote.

#!/opt/bleadperl/bin/bleadperl package Role; package Object; @Object::ISA= ('Role'); sub new { my $class = shift; return bless {}, $class; } package main; my $o = Object->new(); print 'Object DOES Role' if $o->DOES(Role);

I don't see how this is really different than using ->isa(). I understand how roles work in Class::Traits and Moose, but I don't have to use @ISA to setup the relationship. I think i'm missing soming basic or I really don't understand how roles or traits are supposed to work. Can someone point me in the right direction?


Replies are listed 'Best First'.
Re: I don't understand UNIVERSAL::DOES()
by chromatic (Archbishop) on Mar 08, 2007 at 19:10 UTC

    DOES() allows you to test for allomorphism, where objects perform similar duties despite different lineages. isa() allows you to test for inheritance, where objects perform similar duties due to a common lineage.

    What's allomorphism? You've probably encountered it already in Perl 5. Consider, for example, a normal hash reference, a blessed hash, a tied hash reference, and a blessed scalar with hash dereferencing overloading. You have a Perl function that receives as an argument an entity which is one of those four items. You want to access a key and value within that entity as if it were a hash reference--but you don't know precisely what it is.

    To see if you can dereference the reference safely, you have a couple of options. You could use ref to see if the entity is just a hash reference, but that only works for the first one. You could use UNIVERSAL::isa() to check if the entity seems to be a built-in hash (but that definitely fails for the last item and it's really bad style). You could use reftype() from Scalar::Util, but that definitely won't catch the final item.

    Besides that, checking the type is the wrong question--you just want to know if you can dereference the entity as a hash. The real question is "does this entity behave as I expect a hash to behave", not "how does this entity achieve its behavior"?

    In a similar way when dealing with objects (and, in Perl 6, built-in data types which act as objects), DOES() asks "Does this object or class perform this role? I don't care how it does it. I just want to know if it does."

    See Roles: Composable Units of Object Behavior for more information.

    Update: Made the example clearer as an example.

      Consider, for example, a normal hash reference, a blessed hash, a tied hash reference, and a blessed scalar with hash dereferencing overloading.
      [....] You could use UNIVERSAL:­:isa() to check if the entity seems to be a built-in hash (but that fails for the last two items and it's really bad style).

      I believe and my testing confirms that UNIVERSAL::isa() actually works in all but the last case, not just 2 of 4 as you claim.

      I find it ironic, if this is meant to be used for what you claim, that it has repeated the mistake of expecting people to use methods on hash references when methods can't be used on normal hash references (Perl dies due to "unblessed reference"), not even on a reference to an (unblessed) tied hash (so 2 of your 4 cases, including the by-far most common one).

      So it appears we have yet another inconvenient and/or incomplete way to check whether something can be used as a hash. I'll certainly be sticking with UNIVERSAL::isa() for much of my simpler code since it is many times simpler than any of the (possibly) more accurate alternatives, it covers the vast majority of cases, and objects for which it fails can easily be fixed such that it works for them as well; plus it works on non-bleeding-edge versions of Perl.

      - tye        

        So it appears we have yet another inconvenient and/or incomplete way to check whether something can be used as a hash.

        The hash example was an analogy--an analogy of a different type of allomorphism already present in Perl 5.

      Personally I'd /really/ like to see DOES become more useful before 5.10 comes out. I think its a perfect opportunity to fix a bunch of problems. IMO, it should be usable in subroutine form, BUT still respect @ISA for blessed objects, it should handle the question of "can I dereference something in a particular way", and third it should answer "is this a compiled regex". I imagine that something like the following results

      UNIVERSAL::DOES(sub{},'&{}') # return true UNIVERSAL::DOES(qr//,'qr//') # return true UNIVERSAL::DOES([],'@{}') # return true UNIVERSAL::DOES([],'The::Funky::Chicken') # return false UNIVERSAL::DOES([],'UNIVERSAL') # return false @Bar::ISA=qw(Foo); UNIVERSAL::DOES('Bar','Foo') # return true @Bang::ISA=qw(Foo); sub Bang::DOES { return 0 } UNIVERSAL::DOES('Bang','Foo'} # return false

      I actually have a patch to universal.c for this behaviour on the brew right now.


        Instead of perpetuating hackish workarounds that take advantage of an implementation quirk that methods are just functions that take an extra parameter, why not fix the real problem and allow methods on all references via autobox?

        That's much more in line with how it works in Perl 6. It would be nice to keep the semantics of backported features as similar as possible.

        For those of us who can't just presume that 5.010 <= $], how about making UNIVERSAL::isa() check $_[0]->can("isa") if $_[0] is blessed and deferring to that sub if it isn't UNIVERSAL::isa()? Or do you also need to worry about Some::Class::isa() calling UNIVERSAL::isa() and is there no sane way to work around that even from C? Or is there some other reason that this wouldn't be a good idea?

        - tye        

        Wouldn't it be better if the role names matched the ref? I.e. CODE,ARRAY,HASH,REGEX ? And could I see the patch? Just curiousity to see how something like that would be implemented, seems easy enough to implement in perl if you wanted and import it in.

        Here is my stab. I went with a DOES sub returning a list of roles that an object does, makes it easy to make them inheritable...(i.e.

        sub DOES { qw/log print/, shift->SUPER::DOES() }</code). Realy just fo +r my fun and amusment. ;) /me goes off to investigate the various ro +les modules</p> <code> use strict; use warnings; use Test::More qw/no_plan/; { package UNIVERSAL; sub does { return 1 if ref $_[0] eq $_[1]; #if ( eval qq/"@" . ref($_[0]) . "::DOES"/; if (UNIVERSAL::isa($_[0], 'UNIVERSAL') ) { if ($_[0]->can('DOES')) { return 1 if grep { $_[1] eq $_ } $_[0]->DOES(); } else { return 1 if $_[0]->isa($_[1]); } } return 0; } } sub say {print @_, "\n";} {package Foo}; is(UNIVERSAL::does(sub{},'CODE'), 1, 'Code Ref'); is(UNIVERSAL::does(qr//,'Regexp'), 1, 'Regex'); # return tr +ue is(UNIVERSAL::does([],'ARRAY') , 1, 'Array') ; # return + true is(UNIVERSAL::does([],'The::Funky::Chicken'),0 , 'Bad Class name') ;# +return false is(UNIVERSAL::does([],'UNIVERSAL'),0, 'Array isn\'t Univeral') ; + # return false @Bar::ISA=qw(Foo); is(UNIVERSAL::does('Bar', 'Foo'), 1, 'Bar does Foo'); # retur +n true {package Bang; @Bang::ISA=qw(Foo UNIVERSAL); sub DOES { qw(test) }; } is(Bang->does('Foo'), 0, 'Bang doesn\'t Foo'); + # return false { package A; sub DOES { qw/this or that/ }; } { package B; @B::ISA = qw/A/; sub DOES { qw/other/ }; } #B->test(); is(A->does('this') , 1, 'A does this'); # true is(B->does('that') , 0, 'B doesn\'t do that'); # false is(B->does('other'), 1, 'B does other'); # true

        Eric Hodges

      I would like to thank everyone who took time to answer my question.

Re: I don't understand UNIVERSAL::DOES()
by eric256 (Parson) on Mar 08, 2007 at 17:26 UTC

    Reading UNIVERSAL it says that ->does(Role) should always be the same as ISA. I think it will be more as well, but it will return true for anything isa would return true for. It also says that you have to override does for your module. So I think it will be a way for one class to say it does a certain set of methods. I.e. if you have a Logger base then anything that does(Logger) would provide a ->log method. Then things wouldn't have to inherit from Logger to be considered a Logger.

    Eric Hodges
Re: I don't understand UNIVERSAL::DOES()
by InfiniteLoop (Hermit) on Mar 08, 2007 at 18:25 UTC
    Was just reading the latest edition of The Perl Review and Renée Bäcker has an article that discusses UNIVERSAL::DOES. The gist of it is UNIVERSAL::DOES is like "isa", but doesn't depend on the inheritance relationship. For examples refer to the perl core tests, universal.t.
Re: I don't understand UNIVERSAL::DOES()
by Anno (Deacon) on Mar 08, 2007 at 17:24 UTC
    The way I understand the description in perldoc UNIVERSAL, you use DOES to implement roles. By overriding DOES in a class to return true for a role (besides UNIVERSAL) tells the system that your class performs that role. Whatever the consequences of that are... I admit I haven't played with the feature.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://603840]
Approved by Corion
Front-paged by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (2)
As of 2017-09-20 06:27 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (233 votes). Check out past polls.