Jonathan Lang wrote:
> TSa wrote:
Note that I think the conflict detection of role methods prevents the
composition of the equal method through the superclass interface.
Just to make sure we're speaking the same language: by "superclass",
you're referring to classes brought in via "is"; right?
No. I used the term 'superclass interface' as it appears in the
article I referred to. There the role has a type constraint interface
to the uncomposed class which is available through the super keyword.
Let's settle for the terms 'uncomposed class' and 'composed class'.
Then I meant the interface of the role to the uncomposed class.
It might be the case that you should think of the role composition
to happen on a new anonymous class that inherits everything from the
uncomposed class and therefore has a proper superclass interface.
Conflicts between roles prevent the installation of a composed method
and hence leave the task of defining it to the composed class.
I admit that the distinction between these cases can come as a surprise
to class developers. But composing more than one role is equal in
complexity as multiple inheritance. So a warning is a good thing here.
The 'is override' trait might be needed to silence the warning. Well,
and you can switch it off with a pragma or the trait on the class.
The whole point of the article is to calculate the composed class type.
This process is akin to inheritance but needs two implicit type
parameters to model it in F-bounded polymorphism. Free mixins just go
into the composed class unconstraint. Constraint mixins require certain
features from the class. This inevitably shifts the balance between
roles and classes towards roles which is OK in my eyes. In particular
since roles have taken on the meaning of type in Perl 6 and as such
make the stronger claims when used where a type is expected.
If so, you're
correct: the presence of a valid method in any of the roles that a
class composes is sufficient to prevent the dispatch process from ever
reaching any of the superclasses.
I'm more thinking along the lines of closure composition and a flattened
view of the class. That is conceptually the class' namespace directly
maps slot names to methods. That this mapping is searching the
inheritance graph behind the scenes is an implementation detail. In the
case of roles the composed method calls the uncomposed one that's all.
In your terms this means 'dispatch starts at the role' I guess.
On a side track I want to note: the optimizer might munge all combined
methods into an optimized version that doesn't walk the class graph. In
the GenSquare case this method reads
return self.side == $p.side and self.x == $p.x and self.y == $p.y
and is installed in the equal slot of the composed class. I'm
interpreting roles from a type theoretical background. This is viable
because all information needed is available at compile time whereas
classes are open.
Note that the combination process as I see it nicely explains runtime
role composition in the same way as compile time role composition. There
is no special rule that at runtime the role takes precedence or forces
its definition of methods. Also the difference between successive does
and a precomposed role with | falls out naturally. In the latter case
conflicts let role methods drop out and leave the class method as
"disambiguation". But the compiler can easily issue a warning for that.