Austin Hastings wrote: > Jonathan Lang wrote: > > Austin Hastings wrote: > > > There's two ways to look at that. One way is to say: "I'm going to > > > define an interface as being this OTHER thing minus a method." That > > > seems like a positive construction, and supporting it might be > > > desirable. > > > > > > The other way is to say: "Nobody knows what methods call what other > > > methods in their implementation (nor should we know). Therefore, > > > removing methods is forbidden. If you have a conflict of methods, > > > alias them and provide support in the knowledge that any component > > > C<role> that requires the method may call it internally." > > > > Or you could say that when you "exclude" a method, what you're really > > doing is hiding it from everything external to where it's declared, > > while leaving it available to be called internally. Method exclusion > > would be more like declaring a private method in C++ than actually > > removing it from the class or role. This means that a method wouldn't > > be provided to a class that C<does> its role but excludes it itself, > > and thus it wouldn't be used to satisfy the requirements of any other > > roles that the class C<does>. > > Huh? If it's available to be called internally, you've got the same > problem (we were talking about conflict resolution earlier, I think) -- > that you need to know which one to call. > > But if you say "there ain't no bark", either we should complain that > doing a Dog or a Tree demands it, or we should catch the exception at > compile or run time. I'm in favor of the first solution -- you must > provide one -- since that seems to be more in keeping with the general > "role philosophy" that Larry's been emitting.
Ah; I think I see the confusion. You're conflating methods that a role supplies with methods that a role demands. Consider this: role Tree { method bark() {...}; must grow(); # Tree doesn't have a grow method, # but its class will have to have one. }; role Dog { method bark() {...}; must grow(); }; class Trog does Tree does Dog { }; my Trog $spot; $spot.bark; # what happens? $spot.grow; # what happens? OK: when you call $spot.bark, Trog looks for a "bark" method; it finds two: Tree::bark and Dog::bark. Since both methods have been supplied by roles, $spot has no idea which one to use, and throws an exception to that effect. When you call $spot.grow, however, Trog finds _no_ methods; Dog doesn't supply a grow method, and neither does Tree. You get a compile-time error to the effect of "incomplete class declaration - missing a grow method." As such, the code given above is incorrect. Adjusting for this error, we rewrite Trog as follows: class Trog does Tree but no bark does Dog { method grow() {...}; }; Now when we call $spot.bark, it finds Dog::bark but not Tree::bark; therefore, it barks like a dog. When we call $spot.grow, it finds Trog::grow, but there's still no Dog::grow or Tree::grow for it to find; so it grows as only a Trog can. Things get more interesting when one role demands a method and another role supplies it: role Tree { method bark; } role Dog { must bark; method threaten { bark; } } class Trog does Tree does Dog { } my Trog $spot; $spot.threaten; # what happens? When $spot.threaten is called, Trog only finds Dog::threaten, and thus calls it. Dog::threaten then calls $spot.bark; Trog finds a Tree::bark method and nothing else, so $spot barks like a Tree. In other words, when a method supplied by a role calls another method, that call gets dispatched to whatever class that role is a part of. Combining this example with the first one: role Tree { method bark() {...}; must grow(); # Tree doesn't have a grow method, # but its class will have to have one. }; role Dog { method bark() {...}; must grow(); method threaten { bark; } }; class Trog does Tree does Dog but no bark { method grow() {...}; }; my Trog $spot; $spot.threaten; # what happens? When $spot.threaten is called, Trog only finds a Dog::threaten() method, and dispatches the call to it; it in turn calls $spot.bark; Trog finds a Tree::bark but not a Dog::bark (because the bark that would normally be supplied by Dog was suppressed by Trog). There's no ambiguity, so $spot barks like a Tree. So what would happen in the following situation: role Dog { method bark() {...}; method threaten { bark; } }; class Toothless does Dog but no bark { } my Toothless $rover; $rover.threaten; $rover.threaten calls $rover.bark; #Toothless then looks for a bark method... ...and doesn't find one. Hmm... This is only a problem because one of Dog's methods calls the bark method; doing so implicitly causes the role to demand that method on a conceptual basis. Any attribute or method that is used by any of a role's supplied methods is implicitly demanded by that role; but the compiler cannot easily figure out which ones those are. OTOH, do we really want to assume that Dog demands threaten if none of Dog's other methods call it? It would in theory be safe to exclude "threaten" from Dog, even though it wouldn't be safe to exclude "bark". Perhaps _threaten_ can be made to explicitly demand access to bark? That would allow a role's demands to be composed from its methods in a manner similar to how a class' methods are composed from its roles... something like: role Dog { method bark() {...} method threaten must bark() { bark; } } class Toothless does Dog but no bark() { } would be illegal because Dog::threaten() must bark(), thus Dog must bark(), thus Toothless must bark(), but neither Toothless nor its role can bark(). But role Dog { method bark() {...} method threaten must bark() { bark; } } class Toothless does Dog but no threaten() { } would be fine; even role Dog { method bark() {...} method threaten must bark() { bark; } } class Toothless does Dog but no threaten() no bark() { } would be fine (in this case, because removing threaten also removes the demand for bark). But in the case of role Dog { method bark() {...} must bark(); method threaten must bark() { bark; } } class Toothless does Dog but no threaten() no bark() { } You'd still have a problem, because even excluding threaten doesn't exclude Dog's demand to be able to bark. > The notion of "I'm defining it to be {...}" is an interesting one -- > you're explicitly discussing it, so obviously you've thought about it. > But it's not there. I wonder if it wouldn't be easier to say {fail} > instead? IMHO, if you're explicitly discussing it but it's not there, it should somehow be specified as one of the role's demands - either "needs $.attributeName" or "must methodName (signature)" sound right to me. If you have a literal C<{...}> somewhere, you must define that specific routine later - not merely another one that substitutes for it via dispatching. ===== Jonathan "Dataweaver" Lang __________________________________ Do you Yahoo!? Yahoo! Hotjobs: Enter the "Signing Bonus" Sweepstakes http://hotjobs.sweepstakes.yahoo.com/signingbonus