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.
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.
| [reply] |
|
| [reply] |
|
| [reply] |
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
| [reply] [d/l] |
|
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.
| [reply] |
|
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
| [reply] |
|
|
|
|
|
|
|
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?
| [reply] [d/l] [select] |
|
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
| [reply] |
|
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
| [reply] [d/l] |
|
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
| [reply] [d/l] |
|
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
| [reply] |
|
|
|
Re^2: I don't understand UNIVERSAL::DOES()
by rlb3 (Deacon) on Mar 09, 2007 at 17:43 UTC
|
| [reply] |
|
|