No, I haven't. Class::Trait is appealing because I am intimately familiar with it and it's a very complete trait implementation including conflict resolution, proper trait composition, enforcement of requirements, method aliasing, reflection, etc. In short, it has everything we need.
Just glancing at your code suggests you might have the same problem that Class::Trait has:
push @methods, map { [ $r, $_ ] } grep {
*{"${r}::${_}"}{CODE}
} keys %{"${r}::"};
Where do those methods come from? Is there any chance a role might import a function (even as a constant) and have it exported?
| [reply] [d/l] |
Where do those methods come from?
From the role's package. The current specification, as I understand it, is that anything a role can do, the class doing the role can do.
Is there any chance a role might import a function (even as a constant) and have it exported?
If Role->can('foo'), then Class->does('Role') implies Class->can('foo') for all foo in Role. If this isn't the spec, then I can provide a mechanism in Perl6::Roles that will account for that. Let say you define a "@ROLE_METH" or somesuch. But, the current P6 spec, as I understand it, says that the class will get everything.
There is some discussion on p6l about whether or not the role will be able to reserve methods that are labelled private/protected/other, but, AFAIK, @Larry hasn't made a decision one way or the other.
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] [d/l] [select] |
For Perl 6, this is not unreasonable as it's rather easy to disambiguate methods and subroutines. That's not so easy in Perl 5. Instead, consider the example in my root node. I have traits which import functions such as "encode_entities". That's not a method and shouln't be exported by the role. Further, having those things accidentally exported is an encapsulation violation. My classes using roles should not have to worry about what tools those roles use to accomplish their tasks.
However, that's not the only problem I have here. Right now it's cluttering my namespace but having avoided a bug is only a matter of chance. For example, we have a base class called "Mammal" which provides an "eat" method. It's subclassed by "human" of which we have an instance which "does girlfriend":
+--------+
| Mammal | (provides "&eat")
+--------+
|
V
+--------+
| Human | (does "girlfriend")
+--------+
Now imagine that the girlfriend trait imports a function to read files and that function is called "eat". If that's exported to human, there's no conflict because the human doesn't implement "eat" directly. However, we've now silently overridden the inherited "eat" method. The code breaks, there's no hint that it's going to. Everything's bad.
Needless to say, blindly exporting every function causes problems worse than my skills at analogy.
| [reply] [d/l] |