develooper Front page | perl.perl5.porters | Postings from March 2007

Re: [PATCH] Extend functionality of UNIVERSAL::DOES() so it handles additional roles, and works in subroutine form.

Thread Previous | Thread Next
March 15, 2007 12:19
Re: [PATCH] Extend functionality of UNIVERSAL::DOES() so it handles additional roles, and works in subroutine form.
Message ID:
On Thursday 15 March 2007 06:09, demerphq wrote:

Here is the crux of the problem:

The only way you can make a method work on non-invocants is by making it not a 
method anymore, and if you do that to UNIVERSAL::DOES() it no longer belongs 
in UNIVERSAL (because it's not a method) and classes can no longer override 
UNIVERSAL::DOES() (because it's not in all objects and because it's no longer 

UNIVERSAL::DOES() is for invocants only!

If you want something for non-invocants, you have two options.  Make them 
valid invocants or add a function or operator somewhere else.

> > What form of object orientation stores functions--not methods--in the
> > universal base class?
> The same kind of object orientation that we have in Perl.  And the same kind 
> of OO that makes UNIVERSAL::isa() useful as function.

Perl also has semantics that makes code such as:

	my $length = ( 1, 2, 3, 4 );

... occasionally correct.  Sometimes you can write the wrong code and get the 
right answer, but at some point someone ought to step in and say "You really 
oughtn't rely on that."

> It might not have been the best design plan in the world, but it does
> make a certain set of sense, but only if you dont force yourself to
> imagine that OO /must/ work like it works in certain other OOriented
> languages.

I assume that documented and tested features of Perl's OO system such as 
inheritance, method dispatch, and method overriding in derived classes work.

> Its too late now to pretend that there isnt an equivelence between
> subs and methods.

I assume that documented features of Perl's OO system such as method dispatch 
work.  You are free not to assume this; we'll see how far you get removing 
those tests from Perl's core test suite.

> > > I think if that view pervails than UNIVERSAL::DOES in its current form
> > > should be removed from blead. It doesnt really advance things,

> > I think you don't understand how it works.

> Considering ive examined the code with a microscope, and patched it to
> be much more useful, thats a pretty bold claim.

I assume that documented and tested features of Perl's OO system such as 
method dispatch work.  Your patch subverts that for UNIVERSAL::DOES().  My 
conclusion is either that you don't know how or why the code works or you 
don't care.

> Yes true. How do you know that you have an object? Because it didnt
> die when it was used?

I know I have an invocant because it's a method.  You can't call a method 
without an invocant.  That's pretty much how you call methods in OO 
languages--even Perl.

I note, yet again, that this is documented and tested.

> Whats the use case of a routine like this? I imagine it would be much
> the same as UNIVERSAL::ISA() is used for, that is dispatch based on
> ability to fulfil a role. 

The use case is to support systems such as Moose, Class::Trait, and Class::MOP 
as well as objects such as Test::MockObject and Test::MockObject::Extends.

> And in that case non objects are just as 
> likely to need introspection as objects are.

I agree, but you can't perform introspection on them by calling methods on 
them because they're not valid invocants!

Putting a *function* in the universal base class of all objects and calling 
that *function* directly, attempting to detect if the first argument is a 
valid invocant and, if so, attempting to call the overridden method of the 
same name if it exists in the invocant's class or, if the first argument is 
not a valid invocant checking ... something else is a nasty hack.

If you add some other way to check the type of something that may or may not 
be a valid invocant and it calls $thingie->DOES() on valid invocants, that's 
perfectly fine.  I have no objection to that.

> > That's true only because it doesn't *need* helper subs from a different
> > module.
> Actually it does. Or you have to make a big, potentially fatal error,
> kind of assumption that you are being passed an object.

That's pretty exactly how you call methods in Perl.

Are you suggesting that all methods in Perl should check explicitly if they 
have valid invocants?  I think at some point Perl should assume that people 
know what they are doing.  If you really want to break code, go ahead and 
call methods as functions just don't expect things to work reliably.  There's 
a reason Perl's OO documentation talks about method dispatch and its tests 
exercise it.

> And if you are 
> going to make that assumption WHY ARE YOU BOTHERING TO INTROSPECT AT
> ALL? Just use the item as tho it fulfils whatever role you want, and
> if it doesnt fulfil it, well, Perl will ALSO throw an error.

Same reason I run code with strict and warnings on.  It catches errors such as 
homophonic but non-allomorphic cases.  Some people like this.  Duck typing 
has well-known homophonic flaws.

> > You seem to have the impression that DOES() is broken because you can't
> > use it on non-invocants.
> No i think its less than useful because of it. I dont consider it
> broken, i consider it so far less than useful that it doesnt belong in
> core in its current form.

Of course it doesn't work on non-invocants!  You can't call methods on 
non-invocants.  It's a method.  That's why it's in UNIVERSAL.  You can't 
override methods on non-invocants, because they don't have methods--because 
they're non-invocants.

> > > What does it do that isa() doesnt do?

> > It's overridable by subclasses for things such as Test::MockObject,
> > Test::MockObject::Extends, proxy objects, and roles such as in
> > Class::Trait to override and to query without causing people to flipping
> > the bozo bit that trips when they even consider the possibility that the
> > thought of overriding isa() might cross someone's mind.
> So its a workaround for the fact that UNIVERSAL::isa() doesnt
> necessarily return the same thing as $obj->isa().

That's just a nice side benefit.  It's a method that queries the allomorphic 
properties of the invocant.  That's semantically different from "Does this 
invocant descend from this particular ancestor?"

> Which says to me 
> that it should fix the problem with UNIVERSAL::isa() and not just
> provide a way to do an isa lookup that MUST happen via a method call.

The problem with UNIVERSAL::isa() is that it's a method but people call it as 
a function.


Perl 5 has stable, documented, tested, and working method dispatch (barring 
the SUPER:: compile-time brokenness).  If you bypass that, things will break, 
same as if you assume that evaluating a list in scalar context will give you 
the number of elements.

> The problem with this is conceptual. This new keyword has to hook into
> some kind of method in order to facilitate overriding. Which to me
> makes even less sense  than making UNIVERSAL::DOES() behave as my
> patch does.

Do you really think the right solution looks something like this?

		my ($maybe_invocant, $role) = @_;

		if (_is_valid_invocant( $maybe_invocant ))
			my $can_does = $maybe_invocant->can( 'DOES' );
			return $maybe_invocant->$can_does( $role )
				unless $can_does == \&UNIVERSAL::DOES;
			return $maybe_invocant( $role );

		# some switch statement here for different types of references
		# and magic and overloading operations

Note that it's missing a recursion guard if the invocant overrides DOES() but 
calls back into UNIVERSAL::DOES().  Getting this code to work correctly will 
be difficult.

To my mind, if nothing else argues against making UNIVERSAL::DOES() a 
function, not a method, the staggering increase in complexity necessary is a 
fatal flaw.

I have nothing left to contribute to this discussion.

-- c

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About