On Mon, Dec 15, 2003 at 07:02:53PM -0800, Jonathan Lang wrote: : Larry Wall wrote: : > Jonathan Lang wrote: : > : Let's see if I've got this straight: : > : : > : role methods supercede inherited methods; : > : > But can defer via SUPER:: : > : > : class methods supercede role methods; : > : > But can defer via ROLE:: or some such. : : Check, and check. Of course, SUPER:: works well in single inheritence, : but runs into problems of "which superclass?" in multi-inheritence; ROLE:: : would on the surface appear to have that same problem, except that... : : > : conflicting methods from multiple roles get discarded... : > : > They aren't silently discarded--they throw a very public exception. : > (But methods with differing "multi" signatures are not considered to : > be conflicting, I hope.) : : (OK.)
Also, there will be access to the list of call candidates for SUPER:: (and presumably ROLE::) such that the class's method can get explicit control of which super/role method or methods get called. So we can have methods that fail-over to the next candidate. It's just not the default way to resolve multiple methods with the same signature. : > : ...but the class may alias or exclude any of the conflicting methods : > : to explicitly resolve the dispute. : > : > Right. Another possibility is that the class's method could be : > declared to be the default multi method in case the type information : > is not sufficient to decide which role's multi method should be called. : > Maybe if it's declared "multi" it works that way. Otherwise it's just : > called first automatically. : : ...meaning that the question of "which role do you mean?" has already been : addressed by the time the ROLE:: "deference" gets used. No, in this case the ROLE:: deference has already given up on finding a unique role to call, and called the class's method to break the tie, or do something really generic, or call more than one of the role methods, or die. : Although I'm not following what you're saying here in terms of the third : means of disambiguation. Could someone provide an example, please? role Pet { method feed (PetFood $x) {...} } role Predator { method feed (PredatorFood $x) {...} } class DangerousPet does Pet does Predator { } If DangerousPet doesn't define a feed method at all, then we might dispatch to Pet and Predator as if their methods had an implicit "multi". But maybe the actual type of $x is sufficiently ambiguous that we can't decide whether it's more like PetFood or PredatorFood. In that case it would throw an exception, just as any multimethod without a default would. If you define an ordinary method in DangerousPet: class DangerousPet does Pet does Predator { method feed ($x) {...} } then you have the ordinary case. DangerousPet::feed is always called because the class method overrides the role methods. Presumably the class method can dispatch to the role methods if it so chooses. But if you say something like: class DangerousPet does Pet does Predator { multi method feed ($x) {...} } then DangerousPet::feed is called only when multimethod dispatch would have thrown an exception. Alternately, multi's will probably have some way of identifying the default method in any case, so maybe you have to write it something like this: class DangerousPet does Pet does Predator { multi method feed ($x) is default {...} } that leaves the door open for real multi's within the class working in parallel to the roles' methods: class DangerousPet does Pet does Predator { multi method feed ($x) is default {...} multi method feed (DangerousPetFood $x) {...} } Arguably, the role's might be required to declare their methods "multi" if they want to participate in this, but that's one of those things that feel like they ought to be declared by the user rather than the definer. On the other hand, maybe a role would feel that its method *must* be unique, and leaving out the "multi" is the way to do that. But I hate to get into the trap of culturally requiring every method in every role to specify "multi". It's a little too much like the C++ ubiquitous-const problem. : > : trait methods supercede class methods; : > : > I'm not sure traits work that way. I see them more as changing the : > metaclass rules. They feel more like macros to me, where anything : > goes, but you have to be a bit explicit and intentional. : : Well, the question is: the trait is providing a method with the same name : as a method provided by the class, and type information is insufficient to : distinguish between them; which one do I use? In the absence of : additional conflict resolution code, the possible options as I see them : would be: : : 1) the class supercedes the trait : 2) the trait supercedes the class : 3) an ambiguity exception gets thrown : 4) the trait's method can't be called without explicitly naming the trait : : Which of these three ought to hold true? I'm not sure that traits have methods in the same namespace at all. I think they do things *to* the class, and if they want to install a method into the class, they must call a metaclass method to do so. Many traits will be doing evil things to the actual bits in the C struct representing the object. Traits are not definitional but rather operationally defined in the worst way. That's why traits are renegade roles. They don't play by the rules. My hope for unifying traits and superclasses is that, if you call an ordinary class using "is", the wicked thing that it does is insert itself into the "ISA" property of the class. Where that may cause problems if you want to inherit from an existing trait that does something else wicked. But then, traits aren't often going to be inherited anyway, since their purpose is to break the rules. We can maybe inherit from classof(trait) to get around any difficulties. So I'm still thinking we do inheritance with "is" rather than "isa". We just have to keep our names straight. Generally, traits will be lowercase, and true class or role names start with an uppercase letter. : Second, where does the additional conflict resolution code go? In the : trait, in the class, or somewhere else? Traits are for metainformation, so they'll just have to duke it out themselves. Perhaps the metaclass can referee the match. Perhaps not. : > : Am I right so far? Maybe not; I noticed earlier that you've mentioned : > : that roles can be applied at compile-time using "does" or at run-time : > : using "but"; might _that_ be the defining feature as to whether the : > : role supercedes the class or vice versa? "does" supercedes : > : inheritence, "has" and "method" supercedes "does", "is" and "but" : > : supercedes "has" and "method"... : > : > No, I think I'm rejecting that notion as too complicated to keep : > track of from moment to moment, and too much like slatherons in : > policy wishy-washiness. The method precedence won't change from : > compile time to run time. : : OK. My concern is that things like properties add new factors to the : ambiguity issue that you can't expect the class to know about, because : they're being introduced after the class was written. The fact that a : role supercedes inheritence makes sense to me (more precisely, it isn't : counterintuitive); that a class supercedes a role also makes sense to me, : as long as the role was there when the class was defined. But when you : add a role to the class after the fact, as in the case of properties, I : don't see how you can expect the class to be able to resolve the conflict. : What happens when the sticky note that you put on a microwave oven covers : up the display panel? : : It's not so much run-time vs. compile-time as it is "while the class is : being written" and "after the class has been written", and the principle : that he who knows the most about what's going on should make the : decisions. : : Perhaps this could be handled by requiring "sticky-note" roles (of which : properties are a subset) to be explicitly named when their methods are : called? That is, "after the fact" roles don't get flattened into the : class the way that normal roles do. That way, you're not requiring either : the class _or_ the role to resolve the conflict. This would be similar to : the relationship between positional parameters and named parameters, in : that the latter is there to let you add capabilities to an existing : function without disrupting the way that the function normally operates. : (OTOH, that's just about _all_ that it has in common.) I've come around to thinking that C<but> always applies a new subclass (possibly cached). That new subclass can just compose a single role, but it could compose several roles and add new methods of its own to control the multiple roles. See my other message today on this list for more speculation. So the precedence rules are still invariant--a role supercedes inherited methods but is superceded by class methods. We're just changing what C<but> means. It now implies inheritance from the original class, since inheritance is the correct way to override. : > : So how do you resolve conflicts between things that supercede the : > : class? First come first serve (as per slatherons)? : > : > Well, nothing much really supercedes the class. Even traits have : > to be requested by the class, and if you have an entirely different : > metaclass, it's probably declared with a different keyword than : > C<class>. (But sure, multiple traits will have to applied in order : > of declaration, and I don't doubt there will be ordering dependencies.) : : My apologies; I'm apparently a bit weak on my object-oriented terminology. : I'm not quite sure what's being meant here by "metaclass", other than a : vague concept that it's somehow similar to the relationship between logic : and metalogic. Also, I was under the impression that the writers of the : "tTraits" paper that you referred us to disliked "mixins" largely because : they _did_ use an order-of-precedence conflict resolution scheme; surely : their concerns would apply equally well to what we're calling traits? Our traits are for cheating. They're direct hooks into implementational details, encapsulated only by their name. All well-behaved definitions should be using roles and classes instead. The goal is not to avoid ordering dependencies entirely (which is impossible), but to avoid them when they're unnecessary. : > I think the normative way to supercede a class should be to : > subclass it. That's what OO is supposed to be all about, after all. : > If we can keep that orthogonal to role composition, we stand a good : > chance of being able to do a lot of what AOP claims to do without : > the downsides of AOP's own slatheron approach. Or more precisely, : > we can resort to AOP-style wrappers where we really need them, and : > avoid them where we don't. : : As I don't know what AOP is, this is largely lost on me. But I'm all for : keeping various aspects of perl orthogonal to each other if it's : reasonable to do so. Likewise, my main concern isn't so much "how to : supercede a class" as it is "how to keep a class from superceding a role : that it doesn't know about". That's a valid concern, which is what I've been thinking about all week since you said it. : > I'm probably spouting nonsense. I just hope it's good-sounding : > nonsense... Well, I was spouting some amount of nonsense, and I've changed my mind not about roles, but about C<but>. : More importantly, it seems to be _useful_ nonsense. I just hope that _my_ : nonsense is more useful than it is annoying. :) You've spouted a great deal of very useful nonsense. Thanks. :-) Larry