From: Jonathan Lang [mailto:[EMAIL PROTECTED] > 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? >
First, my understanding is that there's a shared requirement (must grow) and a name conflict. Before this can compile, we'll need to provide a grow() method, and we'll need to disambiguate the bark method: there can be only one. Larry and the paper are pretty clear on this point. So I'm thinking class Trog does Tree does Dog { bark => dogBark } { method grow {...}; } > 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. I am uncomfortable with this. My understanding has been that the will be a compile time error, caught when the class declaration is made. Has that changed recently? > 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." I sort-of agree. I don't think it requires a call to grow for the compile time error -- I think it's a declaration time error. (I suppose that the declarator could provide a "secret" {...} declaration for all required methods, but what value does this have?) > 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() {...}; > }; Modulo syntax, this is more right, yes. > Now when we call $spot.bark, it finds Dog::bark but not Tree::bark; > therefore, it barks like a dog. Yep. > 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. Yep. > 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. Right. This seems sensible. > 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. Good so far. > 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". I agree that this is the question. > 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). And this seems to be a valid way to get around the problem. > 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. "Hit jist haint a dawg ifn hit dont bark." But large roles are going to want to say C<must> just once, since (1) that makes for easier, more legible documentation; and (2) if chromatic is correct, most functionality with roles (smaller than classes, recall) will likely be pretty incestuous. So I'm still a little uncomfortable stripping bits out without replacing them. I think I'd rather see a no-op instead. =Austin