Why multi-by-default is a bad idea (was: Re: Object Order of Precedence (Was: Vocabulary))
At 2:21 PM -0800 12/20/03, Larry Wall wrote: On Sat, Dec 20, 2003 at 12:41:10PM -0800, Jonathan Lang wrote: : So what happens if more than one of the candidates is tagged as the : default? The same thing as if none of them was? This could happen if : both Predator and Pet have declared their 'feed' methods as the default. Could blow up, or look for a more generic default that isn't in a tie. The latter seems more fail-soft, since something else of the same name is likelier to know what to do than some random exception handler in who-knows-what dynamic context. It's straightforward enough to pitch an exception at sub definition time. That'll be a compile time error generally, which is likely fine, or an eval/do/require runtime error, which is also fine as anyone doing runtime code loading should be ready to catch compile-class errors. Installing new subs symbolically into symbol tables might be a bit more problematic, but mostly syntactically so it's not my problem. :) : What about making multi dispatches the assumed behavior, with a C : keyword to explicitly shut it off (for the sake of optimization)? That : is, replace the C keyword used to define routines that participate : in multiple dispatching with a C keyword used to define routines : that don't. Now that's...an *interesting* idea. But I'm getting sidetracked. The underlying question is whether "multi" should be the default. And that's still an interesting idea regardless of the syntax. And, IMAO, a very, *very* bad one. I dunno about you, but when I install a sub into a symbol table I fully expect it to be the only one of that name, and if there's an existing sub of that name I expect it to be replaced, not supplemented. (Or have a warning and/or error pitched, that's fine too) This also makes language interoperability somewhat tricky, as it is *not* the default for any other language in our class, including perl 5. That means either we change the default behaviour of perl 5 (which strikes me as bad) or we retain the base default behavior of each language in which case you end up with subs that may or may not be multi depending on the order of inclusion of modules. (if you include two modules that define the same sub in the same namespace, one with multi-by-default and one without) Another unexplored question is how and whether to open up multiple dispatch to more scopes than just the first one in which you find the name I can do lexically-scoped multi-method dispatch tables, the same way we're going to do lexically-scoped method caches, but I'm not sure it's a wise idea. (Well... I'm pretty sure it's an unwise one, but I'm unsure of how correct that is) Could we just leave it as global multimethod subs and methods, and package-local multimethod subs and methods for now? We can always bring in the more insane^Wexpansive version later, in perl 6.2 or something, once we see how things are going and how people are dealing with it. -- Dan --"it's like this"--- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk
Re: [perl] Re: Object Order of Precedence (Was: Vocabulary)
Luke Palmer wrote: Joe Gottman writes: - Original Message - From: "Jonathan Lang" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Saturday, December 20, 2003 3:41 PM Subject: [perl] Re: Object Order of Precedence (Was: Vocabulary) Larry Wall wrote: 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". And the C trait is the tie-breaker when several options are equally likely candidates (in terms of type information); OK. I'm a little leery about calling this trait "default". The problem is that we are already using "default" as a keyword (see the switch statement), and having a trait with the same name as a keyword might confuse users and/or the compiler. Perl's using a top-down compiler now, so it won't be looking for the keyword variant of C after "is". "default" is sufficiently overloaded in English, and by context in Perl, that I don't think anyone will get confused. Not to say that other names for this trait aren't welcome. C
Re: [perl] Re: Object Order of Precedence (Was: Vocabulary)
Joe Gottman writes: > > - Original Message - > From: "Jonathan Lang" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]> > Sent: Saturday, December 20, 2003 3:41 PM > Subject: [perl] Re: Object Order of Precedence (Was: Vocabulary) > > > > Larry Wall wrote: > > > 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". > > > > And the C trait is the tie-breaker when several options are > > equally likely candidates (in terms of type information); OK. > >I'm a little leery about calling this trait "default". The problem is > that we are already using "default" as a keyword (see the switch statement), > and having a trait with the same name as a keyword might confuse users > and/or the compiler. Perl's using a top-down compiler now, so it won't be looking for the keyword variant of C after "is". "default" is sufficiently overloaded in English, and by context in Perl, that I don't think anyone will get confused. Not to say that other names for this trait aren't welcome. Luke
Re: [perl] Re: Object Order of Precedence (Was: Vocabulary)
- Original Message - From: "Jonathan Lang" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Saturday, December 20, 2003 3:41 PM Subject: [perl] Re: Object Order of Precedence (Was: Vocabulary) > Larry Wall wrote: > > 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". > > And the C trait is the tie-breaker when several options are > equally likely candidates (in terms of type information); OK. I'm a little leery about calling this trait "default". The problem is that we are already using "default" as a keyword (see the switch statement), and having a trait with the same name as a keyword might confuse users and/or the compiler. Joe Gottman
Re: Object Order of Precedence (Was: Vocabulary)
On Sat, Dec 20, 2003 at 12:41:10PM -0800, Jonathan Lang wrote: : So what happens if more than one of the candidates is tagged as the : default? The same thing as if none of them was? This could happen if : both Predator and Pet have declared their 'feed' methods as the default. Could blow up, or look for a more generic default that isn't in a tie. The latter seems more fail-soft, since something else of the same name is likelier to know what to do than some random exception handler in who-knows-what dynamic context. : > 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. : : What about making multi dispatches the assumed behavior, with a C : keyword to explicitly shut it off (for the sake of optimization)? That : is, replace the C keyword used to define routines that participate : in multiple dispatching with a C keyword used to define routines : that don't. Now that's...an *interesting* idea. Maybe even worth some punctuation right there in the name. Maybe "is unique" is written: my sub print! ([EMAIL PROTECTED]) {...} meaning: "Always call this one, dang it!" Then maybe "is default" could be my sub print? ([EMAIL PROTECTED]) {...} meaning: "Call this one in case of a tie?" The character would only be in the declaration, not in the call. (Of course, that prevents us from actually using those characters in names like Ruby does, but I'm not sure that's a big loss.) But I'm getting sidetracked. The underlying question is whether "multi" should be the default. And that's still an interesting idea regardless of the syntax. Another unexplored question is how and whether to open up multiple dispatch to more scopes than just the first one in which you find the name, which is the normal Perl6 semantics. I doubt looking everywhere should be the default, but just as a class might call any or all of its roles' methods, a lexically scoped sub might want to call or dispatch to any set of subs of that name that are visible in the current scope. Naming such collections is an interesting problem. Taking scope transitions into account in the distance calculation of multi signatures could be even more interesting. If you define your own multi foo, and there's a global multi foo, how far away is that? Is it worth a level of inheritance in one of the arguments? Is it worth more? Less? Is it worth anything, if you've included both in your set of possible multis to begin with? [Should probably change the Subject if you want reply to this one. In fact, I should probably have split this message into separate responses...] : > 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. : : Unless you're trying to create a variation on an existing trait, of : course. Which might well be done with wrappers rather than via inheritance. I don't think traits are going to have a lot of methods you'd want to inherit. On the other hand, applying a container type to a container is currently done with C, and one might very well want to inherit from a container type, which (like Perl 5 tie classes) might have oodles of inheritable methods. But then, C isn't ambiguous because you're not using it on a class at that point, so maybe it's still okay... : > We can maybe inherit from classof(trait) to get around any difficulties. : : Perhaps you could use C instead of C? More concise and : just as meaningful. Unless C encompasses roles and subtypes but C doesn't... Still, if you're using C on a "real" object, it has to be a class. Probably ought to be C if we want to reserve C for a declarator keyword. Though if we end up with typed refs, we could well end up with a situation where classof($self) returns DangerousPet but typeof($self) returns Predator. : > 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. : : But then, this remains merely a convention; a sloppy programmer (or one : who isn't worried about his code being extensible) could violate it : without the compiler complaining. Certainly. : The only fear that I have here is whether we're violating the "different : things should look different" principle: are traits and superclasses : similar enough to each other to be a
Re: Object Order of Precedence (Was: Vocabulary)
Larry Wall wrote: > Jonathan Lang wrote: > : Larry Wall wrote: > : > Jonathan Lang wrote: > 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. Make micromanaging possible, but not mandatory. Sounds good to me. > : > 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. Oh; OK. > : 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". And the C trait is the tie-breaker when several options are equally likely candidates (in terms of type information); OK. So what happens if more than one of the candidates is tagged as the default? The same thing as if none of them was? This could happen if both Predator and Pet have declared their 'feed' methods as the default. > 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. What about making multi dispatches the assumed behavior, with a C keyword to explicitly shut it off (for the sake of optimization)? That is, replace the C keyword used to define routines that participate in multiple dispatching with a C keyword used to define routines that don't. > 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. When you say "is foo", you're inheriting if foo is a class and you're adding a trait if foo is a trait. OK. This would also imply that the proper way to access a trait's namespace would be identical to the explicit means of accessing a superclass's namespace. > 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. Unless you're trying to create a variation on an existing trait, of course. > We can maybe inherit from classof(trait) to get around any difficulties. Perhaps you could use C instead of C? More concise and just as meaningful. > 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. But then, this remains merely a convention; a sloppy programmer (or one who isn't worried about his code being extensible) could violate it without the compiler complaining. The only fear that I have here is whether we're violating the "different things should look different" principle: are traits and superclasses similar enough to each other to be added to a class by the same means? It might not be a bad idea to include "isa" as a more explicit alternative to "is", with the added benefit that "isa traitname" would be short for "is classof(traitname)". It also occurs to me that traits can be thought of as adjectives (thus the "is " vs. "is a " distinction) - another way to attach an adjective to a noun in English is to prepend it to the noun: my Dog $Spot is red; my black Cat $Tom; my thoughtful $Larry is overworked; where red, black, thoughtful, and overworked are traits. Or is this too much? In a similar vein, what a
Re: Object Order of Precedence (Was: Vocabulary)
Larry Wall writes: > 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. I'd like that roles don't have control over whether their methods are unique, but the class does. That is, multi dispatch would be one of the several disambiguation possibilities for role conflicts. But without specification, name conflicts always result in error. > [...] > > 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. I like that. That allows for nice things like letting a class keep track of its subclasses, or in general doing other tricky things to its subclasses. Inheritance is a logical association, not a functional one, so when your classes are doing nonstandard things, you'd like to modify what inheritance does. Definitely good. Luke
Re: Object Order of Precedence (Was: Vocabulary)
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 expl
Re: Object Order of Precedence (Was: Vocabulary)
Jonathan Lang writes: > Larry Wall wrote: > > 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. (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? I think metaclass is referring to the thing that knows how to associate attributes with their corresponding objects, how do dispatch methods to their corresponding code objects, and whatnot. > > 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". C does a pretty good job of introducing one to AOP, at least the extent to which Perl is capable of it (which is quite a lot). > > I'm probably spouting nonsense. I just hope it's good-sounding > > nonsense... > > More importantly, it seems to be _useful_ nonsense. I just hope that _my_ > nonsense is more useful than it is annoying. :) Luke
Object Order of Precedence (Was: Vocabulary)
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.) > : ...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. Although I'm not following what you're saying here in terms of the third means of disambiguation. Could someone provide an example, please? > : 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? Second, where does the additional conflict resolution code go? In the trait, in the class, or somewhere else? > : 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.) > : 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. (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-or