Re: Role Method Conflicts and Disambiguation
On Nov 2, 2005, at 9:02 PM, Jonathan Lang wrote: Let's say you have this: role A {method foo() { code1; } } role B {method foo() { code2; } } role C does A does B { method foo() { A::foo(); } method bar() { B::foo(); } } Should the following be valid? role D does C { method foo() { B::foo(); } } IMHO, it shouldn't, because D doesn't do B. Not valid in what way? Should this be a fatal error? Are you implying that B is not local to D, so it cannot use it? that somehow disambiguation must be done using one of your local subroles only? I think this is too restrictive, D should be able to freely disambiguate or override using anything it want's to. It need not be related at all to it's subroles. Stevan
Re: Role Method Conflicts and Disambiguation
On Nov 2, 2005, at 9:02 PM, Jonathan Lang wrote: Let's say you have this: role A {method foo() { code1; } } role B {method foo() { code2; } } role C does A does B { method foo() { A::foo(); } method bar() { B::foo(); } } Should the following be valid? role D does C { method foo() { B::foo(); } } IMHO, it shouldn't, because D doesn't do B. Additionally, D most certainly does B. That it does B through a proxy is irrelevant. Think about it this way - if you had Surfer and Dog and SurferDog does Surfer does Dog, wouldn't you want to know that class ScoobyDoo does SurferDog does Dog? If SurferDog doesn't do Dog, how would ScoobyDoo do Dog? I think this is too restrictive, D should be able to freely disambiguate or override using anything it want's to. It need not be related at all to it's subroles. To further expand on this, D's disambiguation of method foo() could be: role D does C { method foo() { Completely::Unrelated::foo() } } Rob
Re: Role Method Conflicts and Disambiguation
HaloO, Rob Kinyon wrote: On Nov 2, 2005, at 9:02 PM, Jonathan Lang wrote: Let's say you have this: role A {method foo() { code1; } } role B {method foo() { code2; } } I think, A and B might just be aliases to the *identical* structural type because the only constraint that both roles impose on their implicit type parameter is the presence of a method foo() on the type of just this parameter. The implementation parts are of course different. In other words the roles A and B are interchangable as far as the structural type checker is concerned! That is there is a theory T{^R} { multi foo (^R) {...} } A ::= T{A}; B ::= T{B}; A::foo.block ::= code1; # or however .block is spelled A::foo.block ::= code2; # or { code2; } ? role C does A does B { method foo() { A::foo(); } method bar() { B::foo(); } } Should the following be valid? role D does C { method foo() { B::foo(); } } IMHO, it shouldn't, because D doesn't do B. Additionally, D most certainly does B. That it does B through a proxy is irrelevant. Think about it this way - if you had Surfer and Dog and SurferDog does Surfer does Dog, wouldn't you want to know that class ScoobyDoo does SurferDog does Dog? Semantic wise or type wise? If SurferDog doesn't do Dog, how would ScoobyDoo do Dog? Assuming that the type system supervises the constraint addition in role composition, a meta information lookup on SurferDog should yield all you need to know about what kind of thing it is! Just think of electrons and the double slit, if you don't require them to know through which slit they passed, they don't bother to remember it ;) I think this is too restrictive, D should be able to freely disambiguate or override using anything it want's to. It need not be related at all to it's subroles. Well, it can't step out of the frame that the constraints of C impose. Actually it can because not all contstraints are computer enforceable but it shouldn't for social reasons. To further expand on this, D's disambiguation of method foo() could be: role D does C { method foo() { Completely::Unrelated::foo() } } Yes, of course. But these are the boring cases. More interesting is something like role X { method foo (: XA $a, XB $b -- XC ) {...} } role Y does X { method foo (: YA $a, YB $b -- YC ) {...} } where the definition of Y should succeed only iff Y::foo : X::foo which requires YC : XC and (XA,XB) : (YA,YB) where : denotes the subtype relation. --
Re: Role Method Conflicts and Disambiguation
On Tue, Nov 01, 2005 at 04:02:04PM -0800, Jonathan Lang wrote: True enough; but it needn't be true that d have the same tools available to resolve the conflicts that c has. There are three ways that a role can deal with a conflict: 1. choose one of a set of available methods to call its own. 2. create a version of its own. 3. pass the buck. In the first case, the question is how we define the set of available methods: do we make the full hierarchy of ancestors available to the role, or do we say that only the immediate parents are available? People keep using the word hierarchy when talking about roles and I keep thinking that it is the one word that definitely does NOT apply. Heirarchies are for classes and inheritance relationships, not roles and composition. In my world view, a role that is composed of two other roles has full view of the methods/attributes defined in the roles that compose it because the landscape is quite flat. There are no hills and valleys. When it finally comes down to composing into a class, the class sees all of the methods/attributes provided by each and every role even the role inside a role inside a roles. Another way to put this would be: should the DOESA list be treated as public or private? (My preference: the DOESA list should be private. You don't lose any capabilities by doing so, other than the capability to access stuff not explicitly declared - a capability that roles don't need, and probably shouldn't have.) If DOESA is private, then d won't have access to anything from a or b without explicitly including them in its own DOESA list. This seems to be restrictive, and it is - but only in the same way that making an attribute private is restrictive. When you say: role A { ... } role B { ... } role C does A does B { ... } role D does C { ... } roles A and B are composed into C at compile time. If both A and B define a method foo(), then there is a conflict (immediately, at compile time) unless you've somehow told perl that it should defer composition until it's actually composing classes. What's really at question I think is whether the _default_ is to compose immediately or to postpone composition until there's a class to compose into. If you have roles that are never composed into classes, then it won't matter if there's a conflict as you can't instantiate a role (not without first turning it into a class). So, on one hand it does make sense to defer composition until there's a class to compose into. But it also makes sense to deal with composition conflicts incrementally too (as roles are composed into other roles). The second case is pretty straightforward. And isomorphic to the first case as it is the resolution to the first case. In the third case, I'd be inclined to say that passing the buck is equivalent to creating an undefined version of your own - that is, not addressing a conflict involving method x is equivalent to saying method x ($arg) { ... }. IOW, a class that does a role that passed the buck is faced with an undefined method complaint if it doesn't do something about it, not an unresolved conflict complaint. Nah, the third case is ye olde standard conflict, just perhaps occurring at a different space+time than had the roles been composed immediately. -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: Role Method Conflicts and Disambiguation
On Wed, Nov 02, 2005 at 16:37:29 -0600, Jonathan Scott Duff wrote: On Tue, Nov 01, 2005 at 04:02:04PM -0800, Jonathan Lang wrote: True enough; but it needn't be true that d have the same tools available to resolve the conflicts that c has. There are three ways that a role can deal with a conflict: 1. choose one of a set of available methods to call its own. 2. create a version of its own. 3. pass the buck. In the first case, the question is how we define the set of available methods: do we make the full hierarchy of ancestors available to the role, or do we say that only the immediate parents are available? People keep using the word hierarchy when talking about roles and I keep thinking that it is the one word that definitely does NOT apply. Heirarchies are for classes and inheritance relationships, not roles and composition. Uh, roles are composed wrt to the hierarchy of who does who. In my world view, a role that is composed of two other roles has full view of the methods/attributes defined in the roles that compose it because the landscape is quite flat. There are no hills and valleys. When it finally comes down to composing into a class, the class sees all of the methods/attributes provided by each and every role even the role inside a role inside a roles. The composition is basically mixin with some exceptions, but there's still a hierarchy roles A and B are composed into C at compile time. If both A and B define a method foo(), then there is a conflict (immediately, at compile time) unless you've somehow told perl that it should defer composition until it's actually composing classes. Class composition also happens at compile time... There's no reason to make the error occur too early for usefulness to be around. As long as it's not too late ;-) -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: : neeyah! pgpABuzitn7pL.pgp Description: PGP signature
Re: Role Method Conflicts and Disambiguation
Jonathan Scott Duff wrote: People keep using the word hierarchy when talking about roles and I keep thinking that it is the one word that definitely does NOT apply. Heirarchies are for classes and inheritance relationships, not roles and composition. In my world view, a role that is composed of two other roles has full view of the methods/attributes defined in the roles that compose it because the landscape is quite flat. There are no hills and valleys. When it finally comes down to composing into a class, the class sees all of the methods/attributes provided by each and every role even the role inside a role inside a roles. -snip- When you say: role A { ... } role B { ... } role C does A does B { ... } role D does C { ... } roles A and B are composed into C at compile time. If both A and B define a method foo(), then there is a conflict (immediately, at compile time) unless you've somehow told perl that it should defer composition until it's actually composing classes. Let's say you have this: role A {method foo() { code1; } } role B {method foo() { code2; } } role C does A does B { method foo() { A::foo(); } method bar() { B::foo(); } } Should the following be valid? role D does C { method foo() { B::foo(); } } IMHO, it shouldn't, because D doesn't do B. -- Jonathan Dataweaver Lang
Re: Role Method Conflicts and Disambiguation
On 11/2/05, Jonathan Lang [EMAIL PROTECTED] wrote: Let's say you have this: role A {method foo() { code1; } } role B {method foo() { code2; } } role C does A does B { method foo() { A::foo(); } method bar() { B::foo(); } } Should the following be valid? role D does C { method foo() { B::foo(); } } IMHO, it shouldn't, because D doesn't do B. Of course it should. To me, role D does C says, not anything that does D must do C, but anything that does D does C. It makes sense if you think of them as interfaces. Say you have a role Complexifiable (that is, this thing can behave like a complex number). Then you have: role Numifiable does Complexifiable { ... } Complexifiable is just an abstraction that is used rarely; most things will implement Numifiable. But if you can behave like a real number, certainly you can behave like a complex number. But you don't want people to have to say: class Foo { does Complexifiable; # wtf? what does that mean does Numifiable; ... } Haskell makes a distinction between the two cases we're talking about here: -- You need to be Complexifiable before you can be Numifiable class (Complexifiable a) = Numifiable a where ... -- Anything that is Numifiable is also Complexifiable -- (by these rules): instance Numifiable a = Complexifiable a where ... To me, does is much more like the latter. Maybe we have constraints on roles that can do the former[1]: role Numifiable { where Complexifiable; ... } Or maybe we don't, and if you need them to implement Complexifiable first, you just leave some methods undefined that they have to define themselves, thus completing the interface. I was always annoyed at Haskell because I had to define an instance of Eq before I could define an instance of Ord, even though Ord's algebra implies that I can compare things for equality (and especially, since Ord defines a total order, that a = b and b = a implies a = b, so it could have implemented that for me). It kind of seems like a Perlish thing to do to blur the two behaviors together. Luke [1] Which is just this theory, without any extensions: theory Numifiable{^T} = Complexifiable{^T} { ... } So it would just be sugar in any case.
Re: Role Method Conflicts and Disambiguation
On Fri, Oct 28, 2005 at 14:19:46 -0400, Stevan Little wrote: Yuval, On Oct 28, 2005, at 10:59 AM, Yuval Kogman wrote: On Thu, Oct 27, 2005 at 22:19:16 -0400, Stevan Little wrote: Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: class MyClass does FooBar {} an exception is thrown. Unless of course MyClass has a foo method, which will disambiguate the conflict. My question then is, can FooBar (the role) disambiguate the foo conflict? IMHO yes, but it doesn't have too. It should be able to leave it to the class to decide. If we allow the class to decide if things break or not, then we potentially allow for the system to be in a very unstable state. A method conflict means that neither method gets consumed, and at that point we have a gapping hole in our class. And compilation will not finish unless the class plugs the hole... role a; role b; role c does a does b; class d does c; If there is a conflict at c, it might be a conflict that c knows how to resolve, or it might be a conflict that c's documentation says the user has to decide on, depending on what they prefer. What I'm saying is that the hole in c can be consumed by d, and if it isn't plugged in either c or d, you get an error. I think it's useful from a role perspective, because that way you can let the user decide on some conflicts. While I can't think of an example, I don't think there is much risk - this doesn't take away functionality. The only issue is that you can compile a role with a conflict and it doesn't complain till you actually use the role (this should be taken care of by writing the simplest of tests for your role). It does open doors for higher complexity composition. Especially if you have a deep hierarchy of role consumption: a b c d \ / \ / e f \- g -/ (role g does e does f, e does a, does b...) Let's say that a and b conflict at 2 points. E can resolve one of them, and leave the other one for g to decide. Perhaps methods in f are used to resolve e with dynamic dispatch: role g does e does f { method conflicts_in_a_and_b ($arg) { given .method_from_role_f($arg) { when Condition { .a::conflicts_in_a_and_b($arg) } defualt { .b::conflicts_in_a_and_b($arg) } } } } This is, IMHO legitimate use. The only restriction should be that when runtime begins no class (or anything that can be instantiated) is allowed to still have a conflict.; In the end, we will have probably looked inside every method defined in Foo, Bar, FooBar and Baz in order to properly write MyClass2. IMHO, this is sort of defeating the usefulness of roles at this point. I disagree - you have to know what a role is actually giving you to exploit it. Too much black boxing makes your programming language seem like a Kafka story. Why? does it introduce bugs? ;-P If you can't see at all into the code you use you eventually run into problems like the shlemiel the painter algorithm for C strings (google to find refs for that, too long to explain here). Frequently when using library code whose documentation is not perfect (and in opensource almost no documentation is ever perfect) I have a great privilige - search.cpan.org has a one click path to seeing the source code of any module i'm considering to install. This allows me to see how something works, estimate how hard it will be to extend it if the need should arise, assess the quality of the code, and make sure I understood things correctly. This spirit of openness should be maintained. You don't have to take apart a role and recompose it and what not, breaking encapsulation. But you should be able to fudge it slightly, because the author of the role might not be getting paid to fix it to your needs, and you may have to get slightly hackish. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me whallops greyface with a fnord: neeyah!!! pgpVo7ZfRizkn.pgp Description: PGP signature
Re: Role Method Conflicts and Disambiguation
Yuval Kogman wrote: Stevan Little wrote: If we allow the class to decide if things break or not, then we potentially allow for the system to be in a very unstable state. A method conflict means that neither method gets consumed, and at that point we have a gapping hole in our class. And compilation will not finish unless the class plugs the hole... role a; role b; role c does a does b; class d does c; If there is a conflict at c, it might be a conflict that c knows how to resolve, or it might be a conflict that c's documentation says the user has to decide on, depending on what they prefer. What I'm saying is that the hole in c can be consumed by d, and if it isn't plugged in either c or d, you get an error. True enough; but it needn't be true that d have the same tools available to resolve the conflicts that c has. There are three ways that a role can deal with a conflict: 1. choose one of a set of available methods to call its own. 2. create a version of its own. 3. pass the buck. In the first case, the question is how we define the set of available methods: do we make the full hierarchy of ancestors available to the role, or do we say that only the immediate parents are available? Another way to put this would be: should the DOESA list be treated as public or private? (My preference: the DOESA list should be private. You don't lose any capabilities by doing so, other than the capability to access stuff not explicitly declared - a capability that roles don't need, and probably shouldn't have.) If DOESA is private, then d won't have access to anything from a or b without explicitly including them in its own DOESA list. This seems to be restrictive, and it is - but only in the same way that making an attribute private is restrictive. The second case is pretty straightforward. In the third case, I'd be inclined to say that passing the buck is equivalent to creating an undefined version of your own - that is, not addressing a conflict involving method x is equivalent to saying method x ($arg) { ... }. IOW, a class that does a role that passed the buck is faced with an undefined method complaint if it doesn't do something about it, not an unresolved conflict complaint. -- Jonathan Dataweaver Lang
Re: Role Method Conflicts and Disambiguation
On 11/1/05, Jonathan Lang [EMAIL PROTECTED] wrote: In the third case, I'd be inclined to say that passing the buck is equivalent to creating an undefined version of your own - that is, not addressing a conflict involving method x is equivalent to saying method x ($arg) { ... }. IOW, a class that does a role that passed the buck is faced with an undefined method complaint if it doesn't do something about it, not an unresolved conflict complaint. Well, I think that the two are pretty much the same thing. I mean, you have to do the same thing to fix it: define your own version. Given that, it's probably best to call it unresolved conflict, so people don't stare at the role they are including and its definition of the method and yell at the compiler IT'S RIGHT THERE!. It's just an error message issue. Luke
Re: Role Method Conflicts and Disambiguation
1. choose one of a set of available methods to call its own. 2. create a version of its own. 3. pass the buck. #1 and #2 are identical. Stevan and I have always viewed #1 as a special case of #2. If you want to choose a method to call, then create a method of your own and have it wrap the one you want to call. The benefit here is that you can do more than just pick a method. Let's say that you have a conflict and the correct behavior is to do them all, but in a certain way. Or, maybe the correct behavior is to provide a limited API over one version. Maybe, there'll be some sugar to allow #1 to be its own syntax, but it should be viewed as a #2. Rob
Role Method Conflicts and Disambiguation
Rob Kinyon wrote: 1. choose one of a set of available methods to call its own. 2. create a version of its own. 3. pass the buck. #1 and #2 are identical. Stevan and I have always viewed #1 as a special case of #2. If you want to choose a method to call, then create a method of your own and have it wrap the one you want to call. The benefit here is that you can do more than just pick a method. Let's say that you have a conflict and the correct behavior is to do them all, but in a certain way. Or, maybe the correct behavior is to provide a limited API over one version. Maybe, there'll be some sugar to allow #1 to be its own syntax, but it should be viewed as a #2. You're right. But this obscures the point that I was trying to make: we need to decide what set of methods are available when disambiguating. Is the DOESA list public or private? Should the role be able to look up any public method that any of its ancestors have, or should it be restricted to just the methods that its parent roles have? Given the flattened nature of composition, I feel that the latter is more appropriate. -- Jonathan Dataweaver Lang
Re: Role Method Conflicts and Disambiguation
On Thu, Oct 27, 2005 at 22:19:16 -0400, Stevan Little wrote: Hello all, I have a question about method conflict resolution works for roles, and I cannot seem to find this in any of the Apoc/Syn documents. Here is the basic issue: role Foo { method foo { ... } method bar { ... } # we will use this later :) } role Bar { method foo { ... } } role FooBar { does Foo; does Bar; } Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: class MyClass does FooBar {} an exception is thrown. Unless of course MyClass has a foo method, which will disambiguate the conflict. My question then is, can FooBar (the role) disambiguate the foo conflict? IMHO yes, but it doesn't have too. It should be able to leave it to the class to decide. role FooBar { does Foo; does Bar; method foo { ... } } Now, on the surface this seems obvious, of course the FooBar role should be able to disambiguate. However, when we expand the example other issues show up. role Baz { does Foo; } class MyClass2 does FooBar does Baz {} # Will this die? splice But thats not all, we have a potential problem with foo again. Baz will provide foo from Foo, but FooBar will provide it's own foo (which we used to disambiguate). So our disambiguation is not ambiguated again. /splice yes, since the methods are coming from different context, and they may have different meanings. While FooBar::foo resolves the conflict from the assimilation of Foo::foo and Bar::foo, it does it only within FooBar. If, OTOH we have a diamond inheritence: role A { method foo { ... } } role B does A {}; role C does A {}; class D does A does B { }; I'm not sure we need to resolve the conflict. Now, since MyClass2 actually does Foo twice, does that mean bar creates a conflcit? Since bar would be found through FooBar and Baz. I would think the answer here would be no, and that we would build some kind of unique list of roles so as to avoid repeated consumption like this. No conflict - an assimilated role is identicle to itself even through different paths since it doesn't get access to private or protected member data of it's consuming roles. If, on the other hand, we have role X trusts Foo does Foo { has $:foo; } role Y trusts Foo does Foo { has $:foo; } role Foo { method foo { $:foo++; } } class C does X does Y { } there is a conflict, because Foo is reaching into either X's private member $:foo or Y's private member $:foo (btw, they should never conflict with each other). The moment a method will have behavior that is different in two different role consumptions it will be conflicting, but this will only happen to a method from the same class if it has some private symbols resolved at role composition time using class friendship. I think it is ambiguous, s A (deceptively) simple solution to this is that MyClass2 needs to disambiguate. But this means that our roles are not really black boxes anymore. In order to properly disambiguate this we need to know where all the foo methods are coming from (Foo, Bar and FooBar), and potentially what is inside these foo methods (especially in the case of FooBar since it is attempting to disambiguate, it's behavior could be very specific). It probably would also become important to know what other methods foo interacts with since we potentially have 3 different expected versions of foo. method foo is Foo::foo; or method foo (*$a) { ./Foo::foo(*$a) } or foo ::= Foo::foo; In the end, we will have probably looked inside every method defined in Foo, Bar, FooBar and Baz in order to properly write MyClass2. IMHO, this is sort of defeating the usefulness of roles at this point. I disagree - you have to know what a role is actually giving you to exploit it. Too much black boxing makes your programming language seem like a Kafka story. So what do you all think?? Role method conflicts should only be dealt with if the user knows what the roles are actually doing anyway. This will probably be familiar code, and if not it warrants familiarity. I don't think we can let the user use library code without being aware of the library code internals at all. Abstraction that works like this is usually either crippled or useless. 90% of the time you don't want to know, but there are exceptions. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me sushi-spin-kicks : neeyah pgpuXeiEdVhEc.pgp Description: PGP signature
Re: Role Method Conflicts and Disambiguation
On Thu, Oct 27, 2005 at 10:19:16PM -0400, Stevan Little wrote: I have a question about method conflict resolution works for roles, and I cannot seem to find this in any of the Apoc/Syn documents. Here is the basic issue: role Foo { method foo { ... } method bar { ... } # we will use this later :) } role Bar { method foo { ... } } role FooBar { does Foo; does Bar; } Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: If we say that the roles Foo and Bar are composed into the role FooBar and that method conflicts trigger an exception at composition time (whether composed into a role or class), then your above declaration of FooBar will trigger an exception and force the user to resolve the conflict. class MyClass does FooBar {} an exception is thrown. Unless of course MyClass has a foo method, which will disambiguate the conflict. My question then is, can FooBar (the role) disambiguate the foo conflict? role FooBar { does Foo; does Bar; method foo { ... } } Now, on the surface this seems obvious, of course the FooBar role should be able to disambiguate. I agree. Methods declared explicitly in FooBar should trump methods that have been composed into FooBar (just as it would if FooBar were a class) However, when we expand the example other issues show up. role Baz { does Foo; } class MyClass2 does FooBar does Baz {} # Will this die? I think yes. FooBar has a method foo() and so does Baz. They may be the same foo() underneath because that's how the FooBar role decided to resolve the method conflict, but Baz doesn't know that. Though, the perl compiler needs to keep track of the composition history of classes/roles to provide useful error messages. Now, since MyClass2 actually does Foo twice, does that mean bar creates a conflcit? Since bar would be found through FooBar and Baz. I would think the answer here would be no, and that we would build some kind of unique list of roles so as to avoid repeated consumption like this. In my world view it doesn't to Foo twice, it does FooBar (which is a composed of Foo and Bar, but MyClass doesn't know the origin of the methods) and it does Baz. Since the Baz role and the FooBar role have the same methods because they were both composed of Foo, the perl compiler will spit out some messages letting you know that you've built a class from a doubly composed role. But if you happen to do class Foo does Bar does Bar { ... } perl should be nice enough not to yell at you for doing the Bar role twice. In the case where really do want the added bits of the Baz role along with the added bits of the FooBar role (including that disambiguation), but perl is carping because most of the methods are the same (having both been composed from Foo), then 1) it's probably a sign that you need to refactor and 2) you can always use delegation to get the desired behavior (assuming I'm correct in that you can delegate to specific roles at composition time as a mechanism for disambiguation). -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: Role Method Conflicts and Disambiguation
On Fri, Oct 28, 2005 at 04:59:18PM +0200, Yuval Kogman wrote: If, OTOH we have a diamond inheritence: You mean composition. There is no role inheritance :) role A { method foo { ... } } role B does A {}; role C does A {}; class D does A does B { }; I'm assuming you meant class D does B does C { ... } I'm not sure we need to resolve the conflict. It depends on when composition happens. If A is composed immediately into B and C as soon as the role is processed, then when D is being composed, all it sees are a foo method from the B role and a foo method from the C role. If, however, roles aren't composed into other roles but only into classes, then I think that class D does B does C { ... } would be equivalent to class D does A does B does C { ... } since the roles B and C would carry the uncomposed A role along with them. Now, since MyClass2 actually does Foo twice, does that mean bar creates a conflcit? Since bar would be found through FooBar and Baz. I would think the answer here would be no, and that we would build some kind of unique list of roles so as to avoid repeated consumption like this. No conflict - an assimilated role is identicle to itself even through different paths since it doesn't get access to private or protected member data of it's consuming roles. If, on the other hand, we have role X trusts Foo does Foo { has $:foo; } Quoth A12: It's not clear whether roles should be allowed to grant trust. In the absence of evidence to the contrary, I'm inclined to say not. We can always relax that later if, after many large, longitudinal, double- blind studies, it turns out to be both safe and effective. Has that changed? Also, I don't think that role is something that can be trusted :) A12/S12 only talk about trusting classes. role Y trusts Foo does Foo { has $:foo; } role Foo { method foo { $:foo++; } } class C does X does Y { } there is a conflict, because Foo is reaching into either X's private member $:foo or Y's private member $:foo (btw, they should never conflict with each other). Er, there is only one $:foo. X doesn't have any private members, nor does Y (because they're roles). Only C has private members. So, modulo the multiple composition of Foo, I don't think there's a conflict. -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: Role Method Conflicts and Disambiguation
On Fri, Oct 28, 2005 at 10:38:31 -0500, Jonathan Scott Duff wrote: You mean composition. There is no role inheritance :) Oops ;-) I'm assuming you meant class D does B does C { ... } I always do that crap... =( I'm not sure we need to resolve the conflict. It depends on when composition happens. If A is composed immediately into B and C as soon as the role is processed, then when D is being composed, all it sees are a foo method from the B role and a foo method from the C role. If, however, roles aren't composed into other roles but only into classes, then I think that class D does B does C { ... } would be equivalent to class D does A does B does C { ... } since the roles B and C would carry the uncomposed A role along with them. I think of class inheritence (mixin or otherwise) as an N-ary tree with ordered branches. That is, if you inherit two classes, one comes after the other. Roles differ because the tree is unordered and all roles assimilated into a specific consumer must be treated equally (and will thus conflict). This allows roles to be more reusable than classes for generic tasks, because if there is a conflict it usually implies namespace clashes, not extended behavior. Making everything into flattenned set is not a feature. It can be the way things are really implemented later, but it's a lossy representation - information about who 'does' who is lost. In my opinion it's better to compose all role information in the largest chunks possible (read - as late as possible in a given linkage/compilation cycle) for the same reasons that JIT optimizations are more effective: you know much more. However, care must be taken to ensure that this is not too late, to the point that the user does not get useful information. Quoth A12: It's not clear whether roles should be allowed to grant trust. In the absence of evidence to the contrary, I'm inclined to say not. We can always relax that later if, after many large, longitudinal, double- blind studies, it turns out to be both safe and effective. Has that changed? Also, I don't think that role is something that can be trusted :) A12/S12 only talk about trusting classes. The role doesn't grant trust to a class that consumes it - it simply says the role Foo has access to my guts when I assimilate it. Class trust is something slightly more complex, since it's inherited. I think that excert says It's not clear whether roles should be allowed to make a class trust another class when they are consumed. there is a conflict, because Foo is reaching into either X's private member $:foo or Y's private member $:foo (btw, they should never conflict with each other). Er, there is only one $:foo. X doesn't have any private members, nor does Y (because they're roles). Only C has private members. So, modulo the multiple composition of Foo, I don't think there's a conflict. Really? I didn't know that... In that case roles are broken... They will need instance data (that doesn't conflict when it's private) to support the methods they give their consumers. Is there any good reason to not allow roles to introduce member data into a class? -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me whallops greyface with a fnord: neeyah!!! pgp1rNFr6EZDh.pgp Description: PGP signature
Re: Role Method Conflicts and Disambiguation
Yuval, On Oct 28, 2005, at 10:59 AM, Yuval Kogman wrote: On Thu, Oct 27, 2005 at 22:19:16 -0400, Stevan Little wrote: Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: class MyClass does FooBar {} an exception is thrown. Unless of course MyClass has a foo method, which will disambiguate the conflict. My question then is, can FooBar (the role) disambiguate the foo conflict? IMHO yes, but it doesn't have too. It should be able to leave it to the class to decide. If we allow the class to decide if things break or not, then we potentially allow for the system to be in a very unstable state. A method conflict means that neither method gets consumed, and at that point we have a gapping hole in our class. IMHO we should not allow this to be decided by the user in any other way then to disambiguate,.. otherwise we should die very loudly. In the end, we will have probably looked inside every method defined in Foo, Bar, FooBar and Baz in order to properly write MyClass2. IMHO, this is sort of defeating the usefulness of roles at this point. I disagree - you have to know what a role is actually giving you to exploit it. Too much black boxing makes your programming language seem like a Kafka story. Why? does it introduce bugs? ;-P So what do you all think?? Role method conflicts should only be dealt with if the user knows what the roles are actually doing anyway. This will probably be familiar code, and if not it warrants familiarity. Agreed. I don't think we can let the user use library code without being aware of the library code internals at all. Abstraction that works like this is usually either crippled or useless. 90% of the time you don't want to know, but there are exceptions. A little extreme, but I tend to agree with your point. Stevan
Re: Role Method Conflicts and Disambiguation
On Oct 28, 2005, at 11:17 AM, Jonathan Scott Duff wrote: On Thu, Oct 27, 2005 at 10:19:16PM -0400, Stevan Little wrote: I have a question about method conflict resolution works for roles, and I cannot seem to find this in any of the Apoc/Syn documents. Here is the basic issue: role Foo { method foo { ... } method bar { ... } # we will use this later :) } role Bar { method foo { ... } } role FooBar { does Foo; does Bar; } Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: If we say that the roles Foo and Bar are composed into the role FooBar and that method conflicts trigger an exception at composition time (whether composed into a role or class), then your above declaration of FooBar will trigger an exception and force the user to resolve the conflict. Well, if we allow FooBar to die, then we do not allow for possible disambiguation by a class (which is the only place it matters anyway since roles cannot be directly instantiated). I think that not keeping the conflict in suspension until the role is composed will actually restrict the usefulness of roles. I should be allowed to create a role with all sorts of conflicts which I leave for the classes to deal with. Remember that if you create a role with a stub method (method my_stub { ... }) then it acts just like a conflict does in requiring the consuming class to implement it. Stevan
Re: Role Method Conflicts and Disambiguation
On Oct 28, 2005, at 11:38 AM, Jonathan Scott Duff wrote: On Fri, Oct 28, 2005 at 04:59:18PM +0200, Yuval Kogman wrote: If, OTOH we have a diamond inheritence: You mean composition. There is no role inheritance :) role A { method foo { ... } } role B does A {}; role C does A {}; class D does A does B { }; I'm assuming you meant class D does B does C { ... } I'm not sure we need to resolve the conflict. It depends on when composition happens. If A is composed immediately into B and C as soon as the role is processed, then when D is being composed, all it sees are a foo method from the B role and a foo method from the C role. If, however, roles aren't composed into other roles but only into classes, then I think that class D does B does C { ... } would be equivalent to class D does A does B does C { ... } since the roles B and C would carry the uncomposed A role along with them. Personally, I think the later makes the most sense. This fits very well with the roles are just containers idea. A role flattens method, attributes, *and* subroles too. If, on the other hand, we have role X trusts Foo does Foo { has $:foo; } Quoth A12: It's not clear whether roles should be allowed to grant trust. In the absence of evidence to the contrary, I'm inclined to say not. We can always relax that later if, after many large, longitudinal, double- blind studies, it turns out to be both safe and effective. Has that changed? To quote $Larry[0] A12 should be read for entertainment purposes only I know, that scares me too ;) Stevan
Re: Role Method Conflicts and Disambiguation
On Fri, Oct 28, 2005 at 02:29:36PM -0400, Stevan Little wrote: I should be allowed to create a role with all sorts of conflicts which I leave for the classes to deal with. Er, why? I've read this sentence several times and I'm really having trouble grasping why anyone would deliberately create a conflicted role and want to postpone the conflict resolution until later. It seems to me that conflicts should be resolved sooner rather than later. Especially in the light of run-time composition: role A { method foo { ... } ... } role B { method foo { ... } ... } role C does B does A { ... } my Dog $spot; ... $spot does C; Would you rather wait until perl is actually executing that last line of code before finding out there's a conflict or would you rather know there's a conflict at compile-time (when C is immediately composed of A and B)? Yes, I realize that Perl6 already has the problem that run-time composition could cause conflicts, but I want to have a slightly smaller chance of running into it :) -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: Role Method Conflicts and Disambiguation
On Fri, Oct 28, 2005 at 06:23:28PM +0200, Yuval Kogman wrote: On Fri, Oct 28, 2005 at 10:38:31 -0500, Jonathan Scott Duff wrote: Er, there is only one $:foo. X doesn't have any private members, nor does Y (because they're roles). Only C has private members. So, modulo the multiple composition of Foo, I don't think there's a conflict. Really? I didn't know that... In that case roles are broken... They will need instance data (that doesn't conflict when it's private) to support the methods they give their consumers. Is there any good reason to not allow roles to introduce member data into a class? Roles can hold instance data that will be composed into a class. What I'm saying is that if you have two roles: role X { has $:foo; } role Y { has $:foo; } And a class that's composed of them: class Xy does X does Y { ... } That there will not be two slots for $:foo in Xy, but only one. But, I'm probably wrong about this as the X role may have methods that use $:foo in one way and the Y role may have methods that use $:foo in some other, incompatible way, so perhaps there will be a conflict just as when there are 2 methods of the same name. -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: Role Method Conflicts and Disambiguation
On 10/28/05, Jonathan Scott Duff [EMAIL PROTECTED] wrote: Roles can hold instance data that will be composed into a class. What I'm saying is that if you have two roles: role X { has $:foo; } role Y { has $:foo; } And a class that's composed of them: class Xy does X does Y { ... } That there will not be two slots for $:foo in Xy, but only one. But, I'm probably wrong about this as the X role may have methods that use $:foo in one way and the Y role may have methods that use $:foo in some other, incompatible way, so perhaps there will be a conflict just as when there are 2 methods of the same name. The solution I proposed to Steve is somewhat involved, so let me take a moment. In my view of objects, there are only behaviors. An attribute is simply a behavior that will store some value (somehow) when asked and will return the last value stored (somehow) when asked. The whole concept of the attribute is to support behaviors that are (ultimately) used publicly. Hence, from a strict encapsulation POV, the attributes should be private to the definer. As such, a role would have private methods that can only be called by methods defined within that role (stubs aside). Stuff that composes said role into themselves would only be composing the public API, not the private API. Those methods that get composed would be able to access the private methods (attributes or not) that were defined within the role. If this solution were to be enacted, then the role wouldn't pollute the class's attributespace with its attributes and there couldn't be any conflict between roles or the roles and the classes. This also extends to attributes and inheritance - the private concept that C++ has. (I don't agree with the protected concept, but private vs. public is good.) Doing it any other way leads to the following: if A does rA and B isa A and B defines an attribute that conflicts with the one provided by rA, how on earth is that supposed to be detected? Especially given that the inheritance tree of a class can be modified at runtime. Rob
Re: Role Method Conflicts and Disambiguation
On Oct 28, 2005, at 2:54 PM, Jonathan Scott Duff wrote: On Fri, Oct 28, 2005 at 02:29:36PM -0400, Stevan Little wrote: I should be allowed to create a role with all sorts of conflicts which I leave for the classes to deal with. Er, why? I've read this sentence several times and I'm really having trouble grasping why anyone would deliberately create a conflicted role and want to postpone the conflict resolution until later. Well, certain classes might want to deal with the conflict in certain ways. role Foo { method foo { [ 1, 2, 3 ] } } role Bar { method foo { { one = 1, two = 2, three = 3 } } } role FooBar { does Foo; does Bar; method display_foo { self.foo.values.join(', '); } } class MyFooArray does FooBar { foo := Foo::foo; } class MyFooHash does FooBar { foo := Bar::foo; } The conflict here basically allows the user of FooBar to determine what kind of data structure to use, while also providing basic means of displaying the data structure. The example a little contrived, but it might be a useful technique in some areas. It seems to me that conflicts should be resolved sooner rather than later. Especially in the light of run-time composition: role A { method foo { ... } ... } role B { method foo { ... } ... } role C does B does A { ... } my Dog $spot; ... $spot does C; Well why should this be any less dangerous that this: my $spot = (class { does A; does B; }).new(); Which is what it will be desugared into anyway. It might also be possible for the compiler to catch this kind of stuff too. Would you rather wait until perl is actually executing that last line of code before finding out there's a conflict or would you rather know there's a conflict at compile-time (when C is immediately composed of A and B)? To be totally honest, I think if you do runtime role composition with a role which you know has a conflict (I mean I assume you either created that role, or know what it provides before you use it), then you deserve all the pain and suffering associated with it. Yes, I realize that Perl6 already has the problem that run-time composition could cause conflicts, but I want to have a slightly smaller chance of running into it :) Actually, according to the for entertainment purposes only A12, if I do this: $spot does Dog does Cat; I am really doing this: $spot = (class { is (class { does Dog; }); does Cat; }).new(); So it won't actually conflict. Stevan
Re: Role Method Conflicts and Disambiguation
On Oct 28, 2005, at 3:04 PM, Jonathan Scott Duff wrote: But, I'm probably wrong about this as the X role may have methods that use $:foo in one way and the Y role may have methods that use $:foo in some other, incompatible way, so perhaps there will be a conflict just as when there are 2 methods of the same name. Yes, they have to conflict. Attribute conflict rules should be the same as method conflict rules. Stevan
Re: Role Method Conflicts and Disambiguation
On Oct 28, 2005, at 3:45 PM, Rob Kinyon wrote: Doing it any other way leads to the following: if A does rA and B isa A and B defines an attribute that conflicts with the one provided by rA, how on earth is that supposed to be detected? Especially given that the inheritance tree of a class can be modified at runtime. B should not know or care that A does rA, because by the time B gets to A, rA has already been consumed by A. If B does not want it's attributes to conflict,.. make 'em private. If rA is concerned that it's attributes will get mussed with, it should make 'em private too (which IMO would mean that A gets a new private attribute, not that it is privatet to the role). Your method (as we have discussed in the past) means that Roles now have instance specific data, which needs to be stored somewhere, somehow (not too difficult). And our class now needs to keep information about the roles it composed around so that it can dispatch to methods appropriately (also not too bad). This then means that the method dispatcher, when it cannot find a private method locally, needs to check all the roles which were composed into the class to find the correct private method and then call that method (I assuming no method conflicts here, but they can (and will) happen). The method then needs to be able to find and extract the role specific private instance data (this action itself requires some complex access control checks), and then do something with it. Like I said, this is assuming we have no conflicts, because the dispatcher will have a hard time determining which role specific private method named :foo you mean in the context this is all happening in. And if we check for conflicts up front, we start to run into the same issues that we did before. One possible solution might be to only allow methods defined within the role to access the private role methods. But then what happens if that method is in conflict. Here is an example: role Foo { has $:bar; # this is role private data method :get_bar { $:bar } # a role private method method gimme_bar { self.:get_bar() } # the public face to the private role data } class Blah does Foo { method gimme_bar { ... } } At this point :get_bar and $:bar are totally inaccessible. IMO, roles should be just containers, and they should hand over everything they have to the class they are composed into. I also think Subclasses should not care what roles their superclasses consumed, that is personal information for the class only to know. Stevan
Re: Role Method Conflicts and Disambiguation
To me, the distinguishing feature between the role and class concepts has always been that roles lack internal structure: you don't have to worry about any hierarchies of what went into creating the role; you just have to pay attention to what attributes and methods it will add to whatever class you compose it into. If A and B have method foo, and AB does both A and B, then AB should have one of: A's version, B's version, its own version, a virtual pass the buck version, or no version at all; it shouldn't have a hierarchy of versions to pick from. If you compose AB into a class, but you don't want the version of foo that it brings to the table (or if AB went with the pass the buck option), then you should either override it with your own version or explicitly bring in another role that has the version that you want - be it A, B, or Q. If you want to deal with hierarchies of methods, deal with classes instead of roles. -- Jonathan Dataweaver Lang
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
Here's how I see roles. This is just an attempt to formalize our concepts so that the answer becomes an obvious truth rather than a decision. A role is an interface with some default implementations. Here's an example: role Foo { # anything that conforms to Foo must provide a foo() method # if one is not provided, this body is used instead method foo () { say hi } # anything that conforms to Foo must provide a bar() method # if one is not provided, then there is an error method bar () {...} # anything that conforms to Foo must provide a baz() method # if one is not provided, then an instance variable is used instead has $.baz; } role Bar { # anything that conforms to Bar must provide a bar() method method bar() {...} } # this is a conditional: anything that conforms to FooBar also # conforms to Foo and Bar role FooBar { does Foo; does Bar; # the requirement for the implementation of a bar() method is # carried over[1] } This is a sort of suspension. If you ignore the roles Foo and Bar, you have simply a role FooBar that has three requirements: foo(), bar(), and baz(), where foo() and baz() have defaults. When you compose this into an empty class, you will get not a conflict but a missing method error. bar() was not implemented. Now, let's say that bar() was given a default implementation in Foo. Then FooBar would be a fully-defined role with no requirements. Let's say that bar() was given a default implementation in both Foo and Bar. That's a conflict. But let's follow suit and pretend that Foo and Bar didn't exist. If you try to compose in FooBar, you will get an ambiguity, which can be resolved by defining your own bar(). So clearly, FooBar simply has a required foo() again. To summarize, as we'll use this later: Foo:foo() default, bar() default, baz() default Bar:bar() default FooBar: bar() required So then we take that, and say that we can give a default implementation for this now-required method. FooBar may disambiguate. Moving on. role Baz { does Bar; } By my free-derivation (or composition in this case, I guess) principle, Baz is now equivalent to Foo. If you think of them as interfaces, it makes perfect sense. Baz provides no additional implementations, nor imposes any additional requirements, and thus you must do precisely the same things to conform to Baz as to Foo. Baz: bar() default Now: class MyClass does FooBar does Baz { } Here's where the theory proposal comes in, yet again. The definition of a class is just the definition of a role together with a (syntactically impossible) declaration of a concrete type. Creating the class is erroneous if the role is not fully-defined. So let's just look at the role half: role MyClass { does FooBar; does Baz; } Now we ignore the inner workings of FooBar and Baz. This is a good idea, as it lets us refactor freely, as long as it looks the same from the outside. Recall that we had: Baz: bar() default FooBar: bar() required So clearly Baz fulfills FooBar's requirements, and MyClass is a fully-defined role. Okay, how did that happen? What was the formality that we actually used? It was the fact that at each stage of the game, we summarized the defaults and requirements for each role, ignoring the internal makeup (i.e., what roles were composed into it, etc.). And it sounds correct to me. Luke [1] Perhaps we even require declaration. We probably shouldn't, in the name of keeping Perl Perl. Any good documentation system ought to catalog all the dependencies.
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
On 29/10/05, Luke Palmer [EMAIL PROTECTED] wrote: Moving on. role Baz { does Bar; } By my free-derivation (or composition in this case, I guess) principle, Baz is now equivalent to Foo. If you think of them as interfaces, it makes perfect sense. Baz provides no additional implementations, nor imposes any additional requirements, and thus you must do precisely the same things to conform to Baz as to Foo. Baz: bar() default You meant to write 'equivalent to Bar', not Foo, right? Stuart
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
On 10/28/05, Luke Palmer [EMAIL PROTECTED] wrote: Here's how I see roles. This is just an attempt to formalize our concepts so that the answer becomes an obvious truth rather than a decision. A role is an interface with some default implementations. -snip- Now we ignore the inner workings of FooBar and Baz. This is a good idea, as it lets us refactor freely, as long as it looks the same from the outside. I would say that this isn't just a good idea; it's at the core of what distinguishes composition from inheritance. With classes and inheritance, you keep all of the baggage that accumulates at each step, on the off-chance that some as-yet unknown descendant might need it. With composition the only baggage that a role keeps is the baggage that is directly relevant to itself. Whatever gets tossed aside can be composed in separately later on if it's needed. Recall that we had: Baz: bar() default FooBar: bar() required So clearly Baz fulfills FooBar's requirements, and MyClass is a fully-defined role. Okay, how did that happen? What was the formality that we actually used? It was the fact that at each stage of the game, we summarized the defaults and requirements for each role, ignoring the internal makeup (i.e., what roles were composed into it, etc.). And it sounds correct to me. Me, too. -- Jonathan Dataweaver Lang
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
On 10/28/05, Luke Palmer [EMAIL PROTECTED] wrote: [snip] It was the fact that at each stage of the game, we summarized the defaults and requirements for each role, ignoring the internal makeup (i.e., what roles were composed into it, etc.). So, in theory, one should be able to ask any given role What do you provide? as well as What do you require?. (I'm not saying that the syntax should be available, but that the information to answer those questions should be easily calculable.) One concern I have is that I would like to believe that a role is a group of related behaviors that provides a functionality. So, if Foo does roleA and I as the user of Foo asks Do you do roleA? and it replies Yes, then I can make some assumptions about Foo's functionality. Otherwise, this devolves into interfaces. Interfaces suck, especially given as they're limited to naming. Now, if we were to say that all roles, by default, provide multimethods, then conflicts only occur for methods that have identical dispatching semantics. At that point, it's truly a conflict and would need to be resolved by the composer (whether that's a role or a class). Now, it's obvious why a class would have to resolve that conflict. I would say that a role would have to resolve the conflict is that a role should present a consistent API to the rest of the world. In other words, I want to be able to depend on the fact that classA does roleAB means something in terms of the functionality that classA provides to me. I don't want it to be a glorified can() check. That does no-one any good. Rob
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
On 10/28/05, Rob Kinyon [EMAIL PROTECTED] wrote: Now, it's obvious why a class would have to resolve that conflict. I would say that a role would have to resolve the conflict is that a role should present a consistent API to the rest of the world. In other words, I want to be able to depend on the fact that classA does roleAB means something in terms of the functionality that classA provides to me. I don't want it to be a glorified can() check. That does no-one any good. Most certainly. Implicit in a role or a theory is its algebra (though we've talked about QuickCheckish ways to make it explicit). For instance, technically the VectorSpace theory only requires you to define identity, addition, and scalar multiplication. However, in order to honestly do VectorSpace, addition has to be commutative, which is something that the compiler usually is not able to check. And that's why you have to explicitly say that your class does the role, rather than it just inferring that from the methods you provide. You may have named your methods the same, but there's no guarantee that you obey the role's algebra. By saying that your class does the role, you are promising that these methods do indeed obey the necessary algebra. So in a way, it's a kind of glorified can(). But in another way, it does guarante certain kinds of behavior from the implementing objects. If you do a role and override a method that violates what the role is supposed to guarantee, then you don't really do that role, even though you do as far as the compiler knows. It's a conceptual contract, not a computationally rigorous one. On the computational level, a role is little more than an interface with defaults. Luke
Re: Role Method Conflicts and Disambiguation (Theory-theoretic take)
Luke, On Oct 28, 2005, at 9:44 PM, Luke Palmer wrote: It was the fact that at each stage of the game, we summarized the defaults and requirements for each role, ignoring the internal makeup (i.e., what roles were composed into it, etc.). This then imposes somewhat of an ordering with role composition. The role tree will need to be traversed breadth first, and conflicts/ requirements resolved on a level by level basis. And it sounds correct to me. It sounds correct to me as well (in theory,.. get it ,... in theory :P). However, I do worry about how it will work out in practice. Part of the goal of roles (and I have been preaching this one since the hackathon) is that they are easier to use than multiple inheritance, have fewer headaches than mix-ins and are more useful than interfaces. Imposing ordering as described above may make things easier, or it may make things harder, only time and experience will tell. I am going to give this some more thought over the weekend, and see what I come up with. Stevan
Role Method Conflicts and Disambiguation
Hello all, I have a question about method conflict resolution works for roles, and I cannot seem to find this in any of the Apoc/Syn documents. Here is the basic issue: role Foo { method foo { ... } method bar { ... } # we will use this later :) } role Bar { method foo { ... } } role FooBar { does Foo; does Bar; } Now, at this point we have a method conflict in suspension since (according to A/S-12) method conflicts do not throw an error until a role is composed into a class. This means that when I do this: class MyClass does FooBar {} an exception is thrown. Unless of course MyClass has a foo method, which will disambiguate the conflict. My question then is, can FooBar (the role) disambiguate the foo conflict? role FooBar { does Foo; does Bar; method foo { ... } } Now, on the surface this seems obvious, of course the FooBar role should be able to disambiguate. However, when we expand the example other issues show up. role Baz { does Foo; } class MyClass2 does FooBar does Baz {} # Will this die? Now, since MyClass2 actually does Foo twice, does that mean bar creates a conflcit? Since bar would be found through FooBar and Baz. I would think the answer here would be no, and that we would build some kind of unique list of roles so as to avoid repeated consumption like this. But thats not all, we have a potential problem with foo again. Baz will provide foo from Foo, but FooBar will provide it's own foo (which we used to disambiguate). So our disambiguation is not ambiguated again. / Possible Solutions / A (deceptively) simple solution to this is that MyClass2 needs to disambiguate. But this means that our roles are not really black boxes anymore. In order to properly disambiguate this we need to know where all the foo methods are coming from (Foo, Bar and FooBar), and potentially what is inside these foo methods (especially in the case of FooBar since it is attempting to disambiguate, it's behavior could be very specific). It probably would also become important to know what other methods foo interacts with since we potentially have 3 different expected versions of foo. In the end, we will have probably looked inside every method defined in Foo, Bar, FooBar and Baz in order to properly write MyClass2. IMHO, this is sort of defeating the usefulness of roles at this point. Another simple (but maybe slightly unintuitive) solution would be to not allow roles to disambiguate at all. (Quick side note: Doing this means that roles like FooBar (which carry with them a suspended conflict) would be restricted in what types of behaviors they can provide. Basically they would only be able to provide new behaviors which are unrelated to those provided by Foo and Bar. If it were to try to use methods from Foo or Bar, it would really end up needing to disambiguate.) This actually makes MyClass2 somewhat simpler now, since it only need to disambiguate between foo from Foo, and foo from Bar. Of course this is only marginally better, since you would still need to look inside all the methods of Foo, Bar, FooBar and Baz to see how foo is being used so you could disambiguate properly. ... So what do you all think?? Stevan