Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Re: I don't understand UNIVERSAL::DOES()

by chromatic (Archbishop)
on Mar 08, 2007 at 19:10 UTC ( [id://603884]=note: print w/replies, xml ) Need Help??


in reply to I don't understand UNIVERSAL::DOES()

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.

Replies are listed 'Best First'.
Re^2: I don't understand UNIVERSAL::DOES() (hash method?!)
by tye (Sage) on Mar 08, 2007 at 21:52 UTC
    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.

        Thanks.

        - tye        

Re^2: I don't understand UNIVERSAL::DOES()
by demerphq (Chancellor) on Mar 09, 2007 at 16:15 UTC

    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.

    ---
    $world=~s/war/peace/g

      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.

        why not fix the real problem and allow methods on all references via autobox?

        Well, that would address /one/ of my concerns, that is it would not just die on an unblessed reference. But it doesnt address the other issues I mentioned.

        UNIVERSAL::DOES() is a perfect opportunity to address a host of typing related issues in Perl in one routine without burdening ourselves with yet another partial solution. Currently we have (at least) ref, Scalar::Util::reftype, Scalar::Util::blessed, UNIVERSAL::isa, overload::Method, and re::is_regexp(). Thats a lot of routines to know about, and to know how to use in concert properly. UNIVERSAL::DOES() seems like the perfect place to roll them all together into something sensible.

        Also it seems to me that DOES is the /correct/ place to ask "can I use $x as a subroutine reference", and not barf because $x is not UNIVERSAL::ISA($x,'CODE') but rather a blessed array reference that happens to overload code deferencing. DOES is about what one can use an object for after all.

        If we can get a sensible routine into core then people will use it, but if its another 2/3rds solution as so many of the existing routines are I bet it wont get used much at all. And will be in my eyes at least a missed opportunity. Especially as by writing it in C the code can be /much/ faster and accurate than any roll your own type detection code is.

        ---
        $world=~s/war/peace/g

      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        

        Or is there some other reason that this wouldn't be a good idea?

        I think that it could have backwards compatibility issues that would preclude it from happening in UNIVERSAL::isa(). Which is why ive been harping on about making DOES do the right thing.

        ---
        $world=~s/war/peace/g

      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

        This is what i have so far. Warning XS/Internals aware code. And no error checking on the rolename.

        UPDATE: An improved version of this code has been posted to p5p for further review, and maybe application in time for Perl 5.10.

        ---
        $world=~s/war/peace/g

        Wouldn't it be better if the role names matched the ref? I.e. CODE,ARRAY,HASH,REGEX ?

        No. For reasons i explained in my reply to chromatic. And 'REGEX' isnt a type. You will never see it returned from ref() or reftype(). Also, your code doesnt solve the "can i deref this item in a particular way" problem, nor the "does this reference contain regexp magic" problem.

        As for code, yeah, once i get it done ill post it. Currently its a work in progress.

        ---
        $world=~s/war/peace/g

Re^2: I don't understand UNIVERSAL::DOES()
by rlb3 (Deacon) on Mar 09, 2007 at 17:43 UTC

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://603884]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-04-19 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found