TSa wrote:
Secondly I figure you are operating on the extension set a role defines. That is union produces a supertype aka the larger set of instances. This is in accordance to the usual type theory approach. Note that Jonathan operates on the intension set of roles that is union produces a subtype.
Assuming that I understand the terminology correctly, I'll go further and say that one of the big differences between roles and subtypes (using the terms in their perl 6 contexts) is that roles conceptually operate on intension sets - everything about them is defined in terms of their set of capabilities - while subtypes (i.e., objects with "where" clauses) operate on extension sets - they're defined in terms of the valid set of instances. Incidently, I'm using terms like "supertype", "subtype", "superrole", "subrole", etc. strictly due to a lack of a better alternative. "Supertyping" only leads to a larger set of instances because a parameter that asks for the supertype is willing to accept a subtype in its stead.
> Things work a little differently for required methods. When a > superrole requires a method be implemented, we (the language > designers) have a choise to make: it is illegal if the superrole > requires a method that the subroles don't implement or don't > themselves require, or it simply adds the new method to the required > list of the subroles which don't implement it. I'm inclined to say > the former, even though it seems a little more brittle. I can see no brittleness. The role that is supertyped is already fully defined when the superrole is about to be created. Thus it can be checked if required but unimplemented methods---that is ones with a {...} body---are present. These should be rejected on the rational that the pre-existing code can't retroactively be made to implement the method.
That's the brittleness: the set of methods that you are permitted to define in the superrole is restricted to the set of methods in the role being generalized. Since Num doesn't include methods re or im, an attempt to include either method in a Complex superrole would be an error according to this approach, or it would involve changing Num to include methods re and im in the alternative approach. The write-up that I saw in the original message seems to carry the implication of the alternative approach (of back-editing the new methods into the subrole). Note that if you go this route, you really should allow yourself to define multiple versions of the new method: one for the superrole, and one for each of the subroles used to define it. I'd rather not go this route, though.
> It means that > when you are implementing a role in a class, all you have to look at > for what you need to do is the role and the roles that it includes; > you don't need to scan the entire source for superroles which > encompass the one you are implementing. That is the user-friendly way > to put it, but there is an analogous argument for independent > modularity (you don't want the definition of a new superrole to > invalidate objects that already exist). Well spoken.
And agreed.
> Things get a little tricky when you think about "does" and "superdoes" > together: > > role Foo does Bar superdoes Baz {...} > > What does that mean? Okay, Foo is a subset of Bar, and a superset of > Baz. Already by simply declaring this we have mandated that "Baz does > Bar". If Baz doesn't already conform to the interface of Bar, Foo > better fill in the gaps for it.
Sorry, but no. If Baz doesn't already conform to the interface of Bar, you're dead in the water. "Filling in the gaps" amounts to changing Baz, which is not in Foo's purview.
In the thread 'signature subtyping and role merging' I have argued the need of merging method signatures that come in from different superroles. This is less of a problem when creating superroles because one can drop the method from the interface and maintain the subtyping relation. So filling in the gaps is a hard task in general. I mean in your example Foo has to define all methods required by Bar so that Baz has at least the default provided by Foo. The gap between Baz and Bar might also contain unresolvable name clashes that are of course no problem as long as Bar and Baz are unrelated.
This is why I think that having both "does" and "done_by" declarations in a role definition is a mistake: the whole thing becomes a cryptic mess far too quickly.
A thing that is still unclear to me is how the issue of the supertype intercepting lvalue uses of its methods is resolved. Preferably in a declarative style. I mean a simple 'is rw' trait will hardly do because you have to return a complete object of which only an attribute is written to. The former subtype value has to be made available for the lvalue method for constructing the new supertype lvalue.
Look at it according to the normal flow: if you subtype a role with read/write capabilities, the new role cannot have fewer rw capabilities than the role that it's composing has. Reversing this, if a role does not have a particular rw capability, then a superrole cannot add it. This is a variation on the "adding methods to the superrole" debate. Mind you, once you've defined a supertype, you can freely create new subtypes of it, and you can add whatever features you want to those new subtypes, be they lvalue access, new methods, or anything else. IMHO, the correct way to create an unordered Complex role from a Num role is to use supertyping to remove the ordering capabilities from Num, and then use subtyping to add "imaginary component" capabilities to that. Yes, this means that a simple partial ordering check between Complex and Num will result in a "no relation" result; but that's a shortcoming of the type-checking system, not the type definition system. -- Jonathan "Dataweaver" Lang