I'm discussing this here cause the plan is kinda long, and wouldn't work
well in IRC.
First, the problem ...
With Moose::Util::MetaRole, it's quite easy to end up with a metaclass
that is not Moose::Meta::Class. Generaly, it's a subclass of MMC with 1+
roles applied to the subclass. Let's call a subclass that differs from its
parent only by the roles it does a "role-only subclass".
Right now, Moose does not handle these sorts of role-only subclasses well
when trying to determine if two classes have compatible metaclasses. If
two classes each have different role-only subclasses as their metaclass,
Moose complains that these metaclasses aren't compatible.
Here's a more concrete example:
ClassA
|
|
ClassB
ClassB inherits from ClassA. Here's the metaclasses:
Moose::Meta::Class
/ \
/ \
MetaSubA MetaSubB
(for ClassA) (for ClassB)
The two metaclass subclasses are each role-only subclasses (doing the
roles RoleA and RoleB respectively).
When we declare ClassB as a subclass of ClassA, Moose dies. What _should_
happen is that Moose detects that the two metaclasses are role-only, _and_
that they share a common ancestor. Then it can simply make a new metaclass
for ClassB which does both RoleA and RoleB, as a subclass of MetaSubA.
Poof, now they're compatible again.
So that's what I'd like to implement. The algorithm goes like this:
1. Look for a common parent class between the two metaclasses (often this
will end up being Moose::Meta::Class). If there is none, there's an
incompatibility. If there are multiple common parents, give up, because I
don't want to deal with multiple inheritance ;)
2. For each class in the chain from a metaclass to the common parent, see
if that class is a role-only subclass.
3a. If all of these subclasses are role-only subclasses _in both chains_,
we just need to combine all the roles done in both chains, and make a new
subclass for said roles.
3b. If any of the classes in the chains are _not_ role-only subclasses, we
have a real incompatibility, and die just like we do now.
The catch is how to determine if a given metaclass is a role-only
subclass, and this is where we get to changing Class::MOP and Moose to
support this.
The basic algorithm I've thought of to detect a role-only subclass looks
like this. Compare all of this class's attributes and methods to its
parent. For any that are specific to the class, see if they come from a
role.
The last part is what's not really possible with today's CMOP & Moose. We
have no good way of knowing the source of an attribute or method, and I
want to change that.
My plan is the following:
* Whenever we add a method (add_method or alias_method), we need to accept
more than just a name and a sub reference. Instead, we can take a
parameter like "source_role" (in Moose) or maybe just "source_package" to
make it generate and applicable to CMOP.
* We can do the same thing for attributes. Additionally, I'd like to be
able to specify an associated_attribute for methods generated from an
attribute. This I want to add just cause we can, and it seems like useful
info.
There's one potential stumbling block here. Right now, Method objects are
not generated until someone asks for one via an introspection method. To
make all this work I think we'll need to generate them as part of
add_method, or even just accept such an object in add_method. This means
we'll be generating these objects during compile/load time, and paying the
cost up front.
So what do people think of this plan?
I'd like to start working on it soon, because the metaclass compatibility
thing is biting me in actual code, and working around it is kind of a
pain, plus Moose should just get this stuff right anyway.
-dave
/*==========================
VegGuide.Org
Your guide to all that's veg
==========================*/