Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: "Everything but the kitchen sink")
- Original Message > From: Jon Lang > Right. But as they were originally conceived, they were interfaces > that could also handle code reuse, rather than units of code reuse > that could also be used as interfaces. From this perspective, it > makes perfect sense that a role's methods can be overridden as easily > as they are. As originally conceived in Perl 6 or in the original traits papers? In the original research, the purpose of roles was to allow the decoupling of responsibility and behavior (code reuse) found in inheritance-based OO systems. Traits (roles) took over code reuse. > But you make a good point: there are some (a few? most?) programmers > who are going to want to use roles primarily for code reuse, and who > will want it to be a little more difficult to override the code > provided by a role (e.g., requiring the use of supersede and perhaps > augment in order to replace the definition with a new one). Just to give people some real data to play with (our system may not be representative), here's some sample source code and some imformation about our use of roles in the BBC. package PIPs::ResultSource::Series; use Moose; extends 'PIPs::ResultSourceBase::BrandSeries'; with qw( PIPs::ResultSource::Role::DoesParentChildRelationships PIPs::ResultSource::Role::DoesTags PIPs::ResultSource::Role::DoesContentObject PIPs::ResultSource::Role::DoesInspector PIPs::ResultSource::Role::DoesRelatedLinks PIPs::ResultSource::Role::DoesIdentifiers PIPs::ResultSource::Role::DoesChangeEvents ); (The astute reader will not that the base class is awful, but it's been a long, hard slog to get this far). Most of our classes which implement roles have similar preambles, but with different behaviors listed. Other points of interest. Only 11 of 114 classes which implement roles exclude any methods (none use method aliasing) and we currently have 40 roles implemented. Only three classes provide methods which override role's methods, but in the few cases they do, we explicitly exclude the methods from the role to make it clear that we need to do this. We had more overriding of role's methods, but continual refactoring has pushed those into roles. So we're very, very heavily on the "use roles for shared behavior" side. The relative paucity of overridden role methods suggests to me that (for our code), the annoyance of having to be explicit for overridding a role's methods easily offset by how hard it's been to debug this issue. That being said, the pain in debugging might have been a side effect of the fast transformation from a complex inheritance hierarchy to a roles-based system. Cheers, Ovid -- Buy the book - http://www.oreilly.com/catalog/perlhks/ Tech blog- http://use.perl.org/~Ovid/journal/ Twitter - http://twitter.com/OvidPerl Official Perl 6 Wiki - http://www.perlfoundation.org/perl6
Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: "Everything but the kitchen sink")
Daniel Ruoso wrote: > Jon Lang wrote: >> The key to understanding roles is to note that roles don't implement >> methods; classes implement methods. > > Er, while I see your point, Roles are not just interfaces... they are OO > components that can be plugged into other classes. They often are used > for type identity exactly because of that attribute, since you won't be > enforcing any hierarchy. Right. But as they were originally conceived, they were interfaces that could also handle code reuse, rather than units of code reuse that could also be used as interfaces. From this perspective, it makes perfect sense that a role's methods can be overridden as easily as they are. But you make a good point: there are some (a few? most?) programmers who are going to want to use roles primarily for code reuse, and who will want it to be a little more difficult to override the code provided by a role (e.g., requiring the use of supersede and perhaps augment in order to replace the definition with a new one). First and foremost, this distinction between suggested ans mandatory implementation is what I was trying to make a little more explicit in my proposal: a suggested method can be overridden by the class with no extra effort; a mandatory method requires that the class be explicit about the override. The next question is which of these approaches Perl 6 should use with roles. Currently, it's using suggested implementations; what I'm hearing you say is that you'd rather have mandatory implementations. IMHO, there's a time ans place for both; so I was trying to come up with a compromise of sorts: a way of letting the programmer select the approach that most suits his needs. >> Roles define which methods must be implemented, and suggest ways that >> they might be implemented; classes decide which implementation to use. >> Anything that breaks this paradigm is a Bad Thing. > > That's not the common conception in Roles usage, specially in Moose. As > I said, Roles are not just interfaces, they are OO reuseable components. FWIW, I never said that they're "just" interfaces. Also, I question whether that is or is not the common conception of role usage. I readily admit that it isn't so in the programming circles that you travel in; but are you typical of the perl community in this regard? This is not a rhetorical question; the way that we end up addressing this issue hinges on this question: should roles provide suggested implementations by default, or should they provide mandatory implementations by default? Even if Perl is rich enough to provide for both, the decision of which way to go when no explicit decision has been made is an important one. > The spec itself says: > > Classes are primarily for instance management, not code reuse. > Consider using C when you simply want to factor out > common code. Right: roles are preferable to classes when it comes to code reuse. That doesn't necessarily mean that roles are _primarily_ intended for code reuse. They _might_ be; but if so, it's because they've grown beyond their original concept. > The key issue here is Perl 6 wasn't yet used to the extent that > Moose::Roles are, and Moose people have identified that the use of Roles > as reusable components raised the issue when the class inadvertedly > overrides one of the methods that are implemented by one of the composed > roles. You know what? Until Moose was mentioned in this conversation, I had never heard of it. > I did think that this should be the expected behavior, but when the > people that is heavily using it says "it took me a lot of time to > debug", it indicates that there's something wrong with the behavior. > > So now I changed my mind, inheritance is about overriding behavior, so > when you implement a method in the subclass it is a natural thinking > that this should override the superclass, but when you think about it > really carefully this logic doesn't really map well to Roles > (considering roles as OO reuseable components). That may indeed be the case. It's entirely possible that we may want to change things so that roles define mandated methods, and possibly introduce interfaces as a variation of roles that define suggested methods. But we may instead want to keep roles as they are, and define some other variation that works just like a role except that it mandates its methods. And its also possible that I'm fundamentally wrong about this, and that we _don't_ need both approaches available for roles. > That being said, I'd think the following as an interesting solution: > > role R1 { > method foo() {...} # degenerates to interface > } > role R2 does R1 { > method bar() { > # some implementation > } > method baz() { > # some implementation > } > } > > class Bla does R2 { > method foo { > # implementing here is natural, since the role only > # declared a stub, it's even a warning not to implement it > } > supersede method bar
Re: Reusing code: "Everything but the kitchen sink"
On Jul 12, 2009, at 20:15 , David Green wrote: sub nighttime (Canine $rover) { $rover.bark if any(burglars()); } (...) 3) $rover acts like a Canine, but the rest of the original $dogwood arg (the Tree parts) are still there; they just aren't used unless somehow explicitly brought out; for example, by casting $rover to a Tree, or by passing it to some other function that is looking for a Tree object. This is how I'd like it to work, because that's the most flexible. If you haven't declared it as such, this strikes me as a bad thing. Perhaps some kind of declarative syntax that lets you declare that you can take a Dogwood, such that you get an added argument which is undef (for a non-Dogwood) or a Dogwood (or Tree?)? -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allb...@kf8nh.com system administrator [openafs,heimdal,too many hats] allb...@ece.cmu.edu electrical and computer engineering, carnegie mellon universityKF8NH PGP.sig Description: This is a digitally signed message part
Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: "Everything but the kitchen sink")
On 2009-Jul-12, at 12:43 pm, Daniel Ruoso wrote: role R1 { method foo() {...} # degenerates to interface } Just wondering: since merely declaring an interface will be common enough, should we be able to say simply "method foo;" inside a role, and drop the {...}? class Bla does R2 { method foo { # implementing here is natural, since the role only # declared a stub, it's even a warning not to implement it } supersede method bar { # explicitly tells that I want to ignore the implementation # in the role. nextsame wouldn't find the role implementation. } augment method baz { # explicitly tells that I want to provide an additional # implementation besides the one in the role. nextsame would find # the role implementation. } } Works for me. I thought having "suggest" to make it work the other way around sounded useful too, but perhaps you think in practice it wouldn't be worth it? -David
Re: Reusing code: "Everything but the kitchen sink"
On 2009-Jul-10, at 4:37 pm, Jon Lang wrote: This is one of the distinctions between role composition and class inheritance. With class inheritance, the full tree of inherited classes is publicly accessible; with role composition, the methods of the combined role are the only ones that are made available to the class. OK, that's actually about what I was thinking, despite the peculiar way I expressed it. I meant the full names to refer to methods directly in the composed role, not somewhere else. Of course, there's already a way to refer to methods with the same name -- using the long name that includes the signature. So my example should have used "bark(Canine: ...)" and "bark(Tree: ...)"; and whichever one actually gets called depends on whether the invocant does Canine or does Tree. so Dogwood::bark ought to consider its context (am I being called to behave like a Canine, a Tree, or something else?) and decide what to do based on that. If Dogwood::bark isn't defined, you should get an implementation conflict error, because the class failed in its duty to provide an implementation. Yes, and Dogwood::bark could handle it by something like: "if $self.does(Canine) {...} elsif $self.does(Tree) {...}" -- but Perl already knows how to handle multiple dispatch based on type, so I shouldn't have to write it out manually. In fact, this works with Rakudo: you can have both barks if you declare them as multis, and then it will accept them without having to declare a Dogwood::bark. (But of course if you try calling it, you get an "Ambiguous dispatch to multi 'bark'" error, because a $dogwood object equally satisfies both signatures.) (I tried to see what would happen if you cast the $dogwood object to Canine or to Tree, but either Rakudo doesn't do it yet, or I got it wrong.) Needing to say "multi" makes sense if you wanted multiple methods of the same name *within* a role (or class or any other namespace), but I don't think it should be necessary across different Roles. Since they already exist in different namespaces, we know they're supposed to mean different things, and it's a simple fact of life that sometimes the same term will get used in different places for completely different meanings. If you have to do the dispatching manually, I guess that's only a slight annoyance as long as it's possible. (Maybe it's better to force the programmer to do it, not because Perl couldn't, but to prevent potential surprises? Hm.) role R { method foo() { say "foo" } role R1 does R { method bar() { say "bar" } role R2 does R { method baz() { say "baz" } class C does R1 does R1 { } The question is whether or not Rakudo is smart enough to realize that R1::foo is the same as R2::foo, or if it complains that R1 and R2 are both trying to supply implementations for foo. The former is the desired behavior. Conversely, in this case the same name means the same thing, so it does seem perl ought to be able to tell that both foo's are really a single foo() here; since they both come from the same role (R), they have to mean the same thing, and C has to know that it does R. In any case, then the question is how to know what role something does, which is really a question about casting and passing args rather than anything to do with Roles per se. I can't tell just from "$dogwood.bark" which kind of barking is wanted; but I could have Dogwood::bark_like_a_dog() instead, perhaps. However, in sub nighttime (Canine $rover) { $rover.bark if any(burglars()); } I can only call ".bark" because all I know for sure is that I have something which does Canine; if I pass it a $dogwood object, I see three possibilities: 1) $rover in the sub is just the Dogwood object that was passed in, and calling $rover.bark cannot know what to do. I also can't call $rover.bark_like_a_dog or anything else, because that method exists only for Dogwood objects, and the sub doesn't always receive Dogwoods. So I'm stuck, and I don't see any way around that the way things are. 2) $rover does Canine and only Canine -- the Tree-half of $dogwood that was passed in is invisible inside the sub, and thus $rover.bark calls bark(Canine:) which is what we want. (Of course, it calls Dogwood's bark(Canine:) when passed a Dogwood object -- it's not magically jumping back to the original Canine role.) If nighttime() in turn calls something-else($rover), the something-else sub also gets only a Canine object. 3) $rover acts like a Canine, but the rest of the original $dogwood arg (the Tree parts) are still there; they just aren't used unless somehow explicitly brought out; for example, by casting $rover to a Tree, or by passing it to some other function that is looking for a Tree object. This is how I'd like it to work, because that's the most flexible. Maybe there should be "hard casting" and "soft ca
RFC: overriding methods declared by roles (Was: Re: Reusing code: "Everything but the kitchen sink")
Em Sex, 2009-07-10 às 15:39 -0700, Jon Lang escreveu: > The key to understanding roles is to note that roles don't implement > methods; classes implement methods. Er, while I see your point, Roles are not just interfaces... they are OO components that can be plugged into other classes. They often are used for type identity exactly because of that attribute, since you won't be enforcing any hierarchy. > Roles define which methods must be implemented, and suggest ways that > they might be implemented; classes decide which implementation to use. > Anything that breaks this paradigm is a Bad Thing. That's not the common conception in Roles usage, specially in Moose. As I said, Roles are not just interfaces, they are OO reuseable components. The spec itself says: Classes are primarily for instance management, not code reuse. Consider using C when you simply want to factor out common code. The key issue here is Perl 6 wasn't yet used to the extent that Moose::Roles are, and Moose people have identified that the use of Roles as reusable components raised the issue when the class inadvertedly overrides one of the methods that are implemented by one of the composed roles. I did think that this should be the expected behavior, but when the people that is heavily using it says "it took me a lot of time to debug", it indicates that there's something wrong with the behavior. So now I changed my mind, inheritance is about overriding behavior, so when you implement a method in the subclass it is a natural thinking that this should override the superclass, but when you think about it really carefully this logic doesn't really map well to Roles (considering roles as OO reuseable components). That being said, I'd think the following as an interesting solution: role R1 { method foo() {...} # degenerates to interface } role R2 does R1 { method bar() { # some implementation } method baz() { # some implementation } } class Bla does R2 { method foo { # implementing here is natural, since the role only # declared a stub, it's even a warning not to implement it } supersede method bar { # explicitly tells that I want to ignore the implementation # in the role. nextsame wouldn't find the role implementation. } augment method baz { # explicitly tells that I want to provide an additional # implementation besides the one in the role. nextsame would find # the role implementation. } } In the above example, declaring a method without either "supersede" or "augment" would result in a compile-time warning, while using "augment" semantics by default. dainel
Re: Reusing code: "Everything but the kitchen sink"
The key to understanding roles is to note that roles don't implement methods; classes implement methods. Roles define which methods must be implemented, and suggest ways that they might be implemented; classes decide which implementation to use. Anything that breaks this paradigm is a Bad Thing. Where things get slightly fuzzy is that there is one case where a class is implicitly assumed to accept an implementation suggested by a role - namely, if that implementation is the only one available. Otherwise, the role is required to explicitly define a given method's implementation. David Green wrote: > Jonathan Worthington wrote: >> The spec is right in that you need to write a method in the class that >> decides what to do. This will look something like: >> >> method fuse() { self.Bomb::fuse() } > > That makes sense for using the combined role on its own, but can we still > handle separate roles that get mixed together? That is, after defining that > method so I can call $joke.fuse(), can I still call $joke.Bomb::fuse and > $joke.Spouse::fuse as well? This is one of the distinctions between role composition and class inheritance. With class inheritance, the full tree of inherited classes is publicly accessible; with role composition, the methods of the combined role are the only ones that are made available to the class. In effect, a combined role acts as a middle manager that looks over the available implementations and picks one, provides its own alternative, or defers the decision to its boss (i.e., the class or role into which it is composed). Either way, once the class has chosen an implementation, that is the implementation that will be used. As I understand it, the reason for this has more to do with attributes than with methods: with role composition, you want to be able to "cut away" any attributes that have become extraneous to the implementations defined in the class. E.g.: role R { has $foo; } class C does R { method foo() is rw { doIO() } } The idea here is that C has chosen to implement foo by querying an outside source (such as a database) whenever a read request is made of it, and by sending information to an outside source whenever a write request is made. It never refers to the internal state that R defined. As such, there's no reason for C to set aside memory to track an internal state. You can't do this if someone is allowed to explicitly call R::foo from any object of class C, overriding C's choice as to how foo should be implemented. > I'm thinking that it's useful to be able to refer to the fully-qualified > names for anything composed into a role or class; often there will be no > ambiguity, so the full name won't be necessary. If the names aren't unique, > then you can specify them fully, and perhaps add an unqualified "fuse()" > that does one or the other (or both? or neither??) for convenience. That > shouldn't be necessary, I think -- it just means you would have to spell out > exactly which method you wanted. This is "class inheritance" think. In "role composition" think, you should never have to worry about how the composed roles might have done things once composition is complete; you only concern yourself with how the class does things. > In the case Ovid also mentioned where two roles have a method of the same > name because both methods really are doing the same thing, there ought to be > a way to indicate that (though if they both come with implementations, you'd > still have to pick one or write your own). > > Of course, in a perfect world, the common method would come from its own > role: if Foo.fuse and Bar.fuse really mean Foo.Bomb::fuse and > Bar.Bomb::fuse, then doing Foo and Bar together should automatically give > you a single .fuse method (again, at least as far as the interface is > concerned). You have a point: it would be nice if you didn't have to engage in unnecessary conflict resolution. Of course, this may actually be the case already; it all depends on how the compiler decides when to complain about conflicting methods. role R { method foo() { say "foo" } role R1 does R { method bar() { say "bar" } role R2 does R { method baz() { say "baz" } class C does R1 does R1 { } The question is whether or not Rakudo is smart enough to realize that R1::foo is the same as R2::foo, or if it complains that R1 and R2 are both trying to supply implementations for foo. The former is the desired behavior. > I guess being able to create a role by dropping some bits from an existing > role would be useful sometimes, but it seems to lose one of the most useful > benefits of roles: as Jon Lang pointed out, "R1 :without" would > effectively be a new role, one that doesn't do R1. But you want something > to do a role in the first place so that it will work anywhere else there is > code looking for that role. Obviously, someone who explicitly drops methods from a role isn't concerned with the new role be
Re: Reusing code: "Everything but the kitchen sink"
The key to understanding roles is to note that roles don't implement methods; classes implement methods. Roles define which methods must be implemented, and suggest ways that they might be implemented; classes decide which implementation to use. Anything that breaks this paradigm is a Bad Thing. Where things get slightly fuzzy is that there is one case where a class is implicitly assumed to accept an implementation suggested by a role - namely, if that implementation is the only one available. Otherwise, the role is required to explicitly define a given method's implementation. David Green wrote: > Jonathan Worthington wrote: >> The spec is right in that you need to write a method in the class that >> decides what to do. This will look something like: >> >> method fuse() { self.Bomb::fuse() } > > That makes sense for using the combined role on its own, but can we still > handle separate roles that get mixed together? That is, after defining that > method so I can call $joke.fuse(), can I still call $joke.Bomb::fuse and > $joke.Spouse::fuse as well? This is one of the distinctions between role composition and class inheritance. With class inheritance, the full tree of inherited classes is publicly accessible; with role composition, the methods of the combined role are the only ones that are made available to the class. In effect, a combined role acts as a middle manager that looks over the available implementations and picks one, provides its own alternative, or defers the decision to its boss (i.e., the class or role into which it is composed). Either way, once the class has chosen an implementation, that is the implementation that will be used. As I understand it, the reason for this has more to do with attributes than with methods: with role composition, you want to be able to "cut away" any attributes that have become extraneous to the implementations defined in the class. E.g.: role R { has $foo; } class C does R { method foo() is rw { doIO() } } The idea here is that C has chosen to implement foo by querying an outside source (such as a database) whenever a read request is made of it, and by sending information to an outside source whenever a write request is made. It never refers to the internal state that R defined. As such, there's no reason for C to set aside memory to track an internal state. You can't do this if someone is allowed to explicitly call R::foo from any object of class C, overriding C's choice as to how foo should be implemented. > I'm thinking that it's useful to be able to refer to the fully-qualified > names for anything composed into a role or class; often there will be no > ambiguity, so the full name won't be necessary. If the names aren't unique, > then you can specify them fully, and perhaps add an unqualified "fuse()" > that does one or the other (or both? or neither??) for convenience. That > shouldn't be necessary, I think -- it just means you would have to spell out > exactly which method you wanted. This is "class inheritance" think. In "role composition" think, you should never have to worry about how the composed roles might have done things once composition is complete; you only concern yourself with how the class does things. > In the case Ovid also mentioned where two roles have a method of the same > name because both methods really are doing the same thing, there ought to be > a way to indicate that (though if they both come with implementations, you'd > still have to pick one or write your own). > > Of course, in a perfect world, the common method would come from its own > role: if Foo.fuse and Bar.fuse really mean Foo.Bomb::fuse and > Bar.Bomb::fuse, then doing Foo and Bar together should automatically give > you a single .fuse method (again, at least as far as the interface is > concerned). You have a point: it would be nice if you didn't have to engage in unnecessary conflict resolution. Of course, this may actually be the case already; it all depends on how the compiler decides when to complain about conflicting methods. role R { method foo() { say "foo" } role R1 does R { method bar() { say "bar" } role R2 does R { method baz() { say "baz" } class C does R1 does R1 { } The question is whether or not Rakudo is smart enough to realize that R1::foo is the same as R2::foo, or if it complains that R1 and R2 are both trying to supply implementations for foo. The former is the desired behavior. > I guess being able to create a role by dropping some bits from an existing > role would be useful sometimes, but it seems to lose one of the most useful > benefits of roles: as Jon Lang pointed out, "R1 :without" would > effectively be a new role, one that doesn't do R1. But you want something > to do a role in the first place so that it will work anywhere else there is > code looking for that role. Obviously, someone who explicitly drops methods from a role isn't concerned with the new role be
Re: Reusing code: "Everything but the kitchen sink"
On 2009-Jul-7, at 5:28 am, Jonathan Worthington wrote: The spec is right in that you need to write a method in the class that decides what to do. This will look something like: method fuse() { self.Bomb::fuse() } That makes sense for using the combined role on its own, but can we still handle separate roles that get mixed together? That is, after defining that method so I can call $joke.fuse(), can I still call $joke.Bomb::fuse and $joke.Spouse::fuse as well? I'm thinking that it's useful to be able to refer to the fully- qualified names for anything composed into a role or class; often there will be no ambiguity, so the full name won't be necessary. If the names aren't unique, then you can specify them fully, and perhaps add an unqualified "fuse()" that does one or the other (or both? or neither??) for convenience. That shouldn't be necessary, I think -- it just means you would have to spell out exactly which method you wanted. In the case Ovid also mentioned where two roles have a method of the same name because both methods really are doing the same thing, there ought to be a way to indicate that (though if they both come with implementations, you'd still have to pick one or write your own). Of course, in a perfect world, the common method would come from its own role: if Foo.fuse and Bar.fuse really mean Foo.Bomb::fuse and Bar.Bomb::fuse, then doing Foo and Bar together should automatically give you a single .fuse method (again, at least as far as the interface is concerned). I guess being able to create a role by dropping some bits from an existing role would be useful sometimes, but it seems to lose one of the most useful benefits of roles: as Jon Lang pointed out, "R1 :without" would effectively be a new role, one that doesn't do R1. But you want something to do a role in the first place so that it will work anywhere else there is code looking for that role. So supposing: role Canine { method bark { say "ruff"; } }; role Tree { method bark { say "rough" } }; class Dogwood does Canine does Tree { ... }; my Dogwood $dw; sub nighttime (Canine $rover) { $rover.bark if any(burglars()); } sub paper (Canine $rex) { $rex.bark if newspaper(:delivered); } sub paper (Tree $nodes) { $nodes.bark ==> flatten; ... } What happens when I call nighttime($dw)? Obviously, it's meant to call $dw.Canine::bark. Since nighttime() is looking for something that does the Canine role, any method calls in that function can safely be assumed to be short for .Canine::bark (since all we know for sure is that any arg passed in will do Canine, and we can't know it will do anything else). If I want to call paper(), then I would have to cast $dw to either one of the roles, e.g. paper(Tree($dw)), and that would presumably strip off or hide the Canine part of its nature. It doesn't seem unreasonable for any non-Tree attributes to be inaccessible inside paper(Tree), since all it can guarantee is the arg does Tree; but on the other hand, I think it would be OK but would require you to use the fully qualified names for anything non-Tree. If Dogwood defines its own .bark method, I can see it meaning one of two things: either it's yet another bark(), specifically $dw.Dogwood::bark(), that can be used only where we're expecting a Dogwood object. (It might bear no relation to Canine::bark or Tree::bark, although in that case it would probably be a good idea to pick a different name!) Or else, it could mean a consolidation of the two mixed-in .bark's, i.e. $dw.Canine::bark and $dw.Tree::bark would both now be implemented by plain $dw.bark, aka $dw.Dogwood::bark (all three full names would mean the same thing for Dogwood objects). -David
Reusing code: "Everything but the kitchen sink"
Jonathan Worthington wrote in "YAPC::EU and Perl 6 Roles": > More fitting to me would be an adverb to the does trait modifier... > > class C does R1 :without does R2 :without { ... } > > The thing is that in this case, does the class actually do R1 and R2? If you > are going to derive an anonymous role with the methods missing, then answer > is "no". That is, C ~~ R1 would be false. So I think it'd need to act as a > modifier to the action of composition, rather than a modification to the > thing that we're composing. Moving the adverb from the role to the trait modifier would still violate the notion that C ~~ R1; so it doesn't actually gain us anything. Instead, consider the following possibility: "R1 :without" is a separate but related role from "R1". The implicit relationship between them is that R1 ~~ R1 :without. So "C ~~ R1" would be false; but "C ~~ R1 :without" would be true. > I wonder if we'd want to mandate that a method of the name must come from > _somewhere_ otherwise it's an error. At least then you get a promise that a > method of that name exists...which is about all that "it does this role" > tells you as an interface contract anyway. Right. Another way to handle this without establishing "reverse-does" relationships that I describe above would be to say that the adverb doesn't actually remove the method in question; it just suppresses its implementation. That is, given: role R1 { method foo() { say "foo" } } class C does :blocking R1 { ... } this would compose R1 into C, but would discard the implementation of R1::foo while doing so. In the spirit of TIMTOWTDI, both approaches could be available: "R1 :without" acts as an anonymous role that R1 implicitly does, and which is set up almost exactly like R1 except that it doesn't have method foo. This could have other uses besides role composition, such as expanding a web of roles from the specific to the general. "C does :blocking R1" composes R1 into C, but discards foo's implementation while doing so. >> Alternatively, I could see a version of this exclusionary policy being >> done through method delegation, by means of the whatever splat - >> something like: >> >> class C { >> has A $a handles * - (foo, bar); >> has B $b handles * - baz; >> } > > The RHS of the handles is something we smart-match the method name against > (unless it's one of the special syntactic cases). And thus if you care about > performance you probably don't want to be falling back to handles to do your > role composition, since it's kind of the "last resort" after we've walked > the MRO and found nothing. You're right, but for a different reason. Perl 6 has three techniques for code reuse, which I refer to as the "be, do, have" triad: Class inheritance ("to be"): 'is C;' Role composition ("to do"): 'does R;' Attribute delegation ("to have"): 'has A $a handles foo;' Just on a conceptual level, you don't want to fall back on attribute delegation in order to emulate role composition. Still, there are uses for delegating to all or most of an attribute's methods. > Anyway, you'd put something on the RHS maybe > like: > > has A $a handles none() > > But I'm not sure that will fall through to B for anything that A doesn't > define other than those two. You'd perhaps just get a dispatch error if you > said A handles everything but those and it didn't. So it'd probably look > more like... > > has A $.a handles all(any(A.^methods>>.name), none()); > > Which you almost certainly don't want to be writing. ;-) Right; which is why I was looking for a more abbreviated form. If we go with the idea that we're using a Set on the RHS, then '*' could be shorthand for 'Set($a.^methods)', and '&Set::infix:<->' could be the Set Difference operator, with the item, list, or set on the RHS being excluded from the LHS. So: $a handles * - would be short for something like: $a handles Set($a.^methods) - Set("foo", "bar") This use of the Whatever splat is similar to its use in a list index, where '*' behaves as if it were the list's element count. -- Looking this over, I note that the only code reuse mechanism for which we haven't looked at the "everything except..." concept is class inheritance. OTOH, I think that the blocking tool that works for role composition could be adapted for use with class inheritence as well: Class C is :blocking C1 is :blocking C2 { ... } That is, if you were to search the class heierarchy for foo, it would skip the C1 branch in its search. This would have to be treated with care, because it would mean that C doesn't inherit everything from C1. This _would_ break the "anything you can do I can do, too" nature of class inheritence; but I'm not sure how big of a crime that is. Perl generally discourages the use of class hierarchies, and it certainly isn't the preferred means of type-checking. And maybe you could get around this by having the "isa" predicate return a more in