Re: class interface of roles
HaloO, I wrote: And finally the combined class class GenLocSquare is GenSquare does GenPointMixin {} I'm still pondering how one achieves method combination of the role's and the class' methods. Here are two ideas. The first is to use an 'is extended' part in the role that re-opens the class when the role is composed. Question is how such a class extension addresses the unextended class. Does the call($p) do what I hope it does? role GenPointMixin { has Int $.x; has Int $.y; is extended { method equal( ::?CLASS GenEqual: ::?CLASS $p -- Bool ) { return call($p) and self.x == $p.x and self.y == $p.y; } } } The second idea is to subclass an anonymous class when the role is composed. I don't know if this is valid syntax but the anonymous class would provide a proper superclass interface such that the equal method can be combined from the role's definition that goes into the anonymous class and the uncomposed class' version of the equal method. Note that the presence of such a method is guaranteed by the GenEqual type constraint on the invocant type of the method. role GenPointMixin is ::?CLASS { has Int $.x; has Int $.y; method equal( ::?CLASS GenEqual: ::?CLASS $p -- Bool ) { return call($p) and self.x == $p.x and self.y == $p.y; } } Regards, TSa. --
Re: class interface of roles
HaloO, I wrote: role GenPointMixin { has Int $.x; has Int $.y; is extended { That should be spelled 'is also'. How does such an 'is also' block address the former class definition block? Is it OUTER::? Or SUPER::? The latter would mean that class extension is a kind of pseudo-inheritance. Regards, TSa. --
Precedence levels and associativity conflicts (Re: class interface of roles)
Larry (): [...] The non-chaining precedence level is a bunch non-associative operators like .. and cmp. Historically, all operators of a particular precedence level have had the same associativity, so that when you analyze $a op1 $b op2 $c you only have to compare op1 with op2 if they're the same precedence, and given the prededence you don't actually have to look at the individual ops. What would it mean to have a left-associative op at the same precedence level as a right-associative op: $a lefty $b righty $c Who would win? So generally we try not to mix associativity within a precedence level. Short question: what would happen if I as the user would try and define two operators on the same precedence level, and then try to use them in the above way? How far would I get? In other words, I'd do something like this: my sub infix:lefty is assoc('left') ($a, $b) { $a - $b; } my sub infix:righty is assoc('right') ($a, $b) { $a - $b; } say (1 lefty 2 righty 3); # a-ha! will this be -4 or 2 ? Pugs currently dies on evaluating the last line, explaining to me that an ambiguous use of a right associative operator occurred. This might even be a fairly sane behavior. The only alternative I can think of right now would be to disallow even _declaring_ two operators of different associativity on the same precedence level... but that kind of strictitude doesn't sound very perlish. Kindly, -- masak
Re: Precedence levels and associativity conflicts (Re: class interface of roles)
Carl Mäsak wrote: The only alternative I can think of right now would be to disallow even _declaring_ two operators of different associativity on the same precedence level... but that kind of strictitude doesn't sound very perlish. That depends on how you phrase the restriction. If you phrase it as all assoc traits that get applied to operators of equivalent precedence must have the same value, you're right; it doesn't sound very perlish. If you phrase it as associativity is a feature of the precedence level, not the operator and adjust syntax accordingly, it's very perlish. The syntax adjustments would go something like this: remove the 'assoc' trait; let people specify an optional assoc value when they use the 'looser' or 'tighter' traits; and require code to look up the operator's precedence in order to modify its associativity. -- Jonathan Dataweaver Lang
Re: Precedence levels and associativity conflicts (Re: class interface of roles)
Jonathan (), Carl (): The only alternative I can think of right now would be to disallow even _declaring_ two operators of different associativity on the same precedence level... but that kind of strictitude doesn't sound very perlish. That depends on how you phrase the restriction. If you phrase it as all assoc traits that get applied to operators of equivalent precedence must have the same value, you're right; it doesn't sound very perlish. If you phrase it as associativity is a feature of the precedence level, not the operator and adjust syntax accordingly, it's very perlish. The syntax adjustments would go something like this: remove the 'assoc' trait; let people specify an optional assoc value when they use the 'looser' or 'tighter' traits; and require code to look up the operator's precedence in order to modify its associativity. While I'm not in a position whence I can tell if your solution is better than the current or not, I definitely like the practice of changing one's model of the world into one where the problem itself simply ceases to exist. :) -- masak
Re: class interface of roles
On Thu, Oct 19, 2006 at 03:31:18PM -0700, Jonathan Lang wrote: : Larry Wall wrote: : Though actually, now that I think about it, the cascaded notation : in S12 is illegal according to S03, since does is classified as : non-chaining, which implies non-associative. : : Wait a minute. Isn't chaining specifically referring to the idea : that A op B op C implicitly becomes something like A op B B op : C? That would seem to be unrelated to the concept of associativity, : other than the fact that you can't have chaining without : associativity. Why would does have to be chaining in order to be : associative? I was using non-chaining to indicate a particular precedence level. (In actual fact, all precedence levels but chaining are non-chaining, but only one precedence level actually has that name.) The non-chaining precedence level is a bunch non-associative operators like .. and cmp. Historically, all operators of a particular precedence level have had the same associativity, so that when you analyze $a op1 $b op2 $c you only have to compare op1 with op2 if they're the same precedence, and given the prededence you don't actually have to look at the individual ops. What would it mean to have a left-associative op at the same precedence level as a right-associative op: $a lefty $b righty $c Who would win? So generally we try not to mix associativity within a precedence level. For now the conservative thing is probably that we should just leave does as non-associative and make you write $obj.also_does(A,B,C) or some such if you want to get fancy. Larry
Re: class interface of roles
On Fri, Oct 20, 2006 at 04:47:04PM -0700, Larry Wall wrote: : For now the conservative thing is probably that we should just leave : does as non-associative and make you write : : $obj.also_does(A,B,C) : : or some such if you want to get fancy. Presumably $obj does (A,B,C) could also be made to work even with non-associative does. Larry
Re: class interface of roles
Larry Wall wrote: Presumably $obj does (A,B,C) could also be made to work even with non-associative does. Right. Note that you _do_ want to be able to do something to the effect of ordered composition in a single statement, though: role A { ... method m { ... } } $x does A; A.m will override $x.m - assuming that $x.m exists at all. As such, you end up with an anonymous class with an undefined method - a distinct no-no. Maybe what you need to say is that $x does ... is a macro that treats what follows as part of an anonymous class definition. So $x does A does B { method m { doit } } ^^ would be equivalent to something like $x.WHAT := class is $x.WHAT does A does B { method m { doit } } ^^ You'd need to have the macro work some magic involving curly-braces and terminating semicolons, so that $x does A; doit; ... becomes $x.WHAT := class is $x.WHAT does A { } doit; ... instead of $x.WHAT := class is $x.WHAT does A { doit; ... } And you'd have to decide what to do with $x does role { ... } { ... } Should this become $x.WHAT := class is $x.WHAT does role { ... } { ... } or $x.WHAT := class is $x.WHAT does role { ... } { } { ... } ? But otherwise, this approach allows runtime composition to use exactly the same syntax and semantics as compile-time composition as soon as you hit infix:does. -- Jonathan Dataweaver Lang
Re: class interface of roles
Larry Wall schreef: I suspect ordered composition is going to be rare enough that we can simply dehuffmanize it to $x does A; $x does B; $x does C; Maybe use a list-like notation? $x does (A, B, C,) ; $x does (A ; B ; C) ; $x does [A, B, C,] ; $x does [A ; B ; C] ; -- Groet, Ruud
Re: class interface of roles
HaloO, Larry Wall wrote: You've got it inside out. Unordered is just does A | B | C or some such, the | there really being coerced to a set construction, not a junction. In fact, would work just as well. I only used | because it's more readable. Autocoercion of junctions to sets is, of course, conjectural. You really have does set(A,B,C) I would like does A B C mean the intersection type of A, B and C. That is a supertype of all three roles. In addition we might need negation to get what Jonathan Lang envisoned for the Complex type that does Num !Comparable. IOW, I'm opting for a role combination syntax by means of logical operators that operate on the intension set of the involved roles. Could we get that? BTW, the synopsis reserve Foo[Bar] and Foo{Bar} for type theoretical operations. The former is used for parametric types. How is the latter used? Regards, TSa. --
Re: class interface of roles
HaloO TSa wrote: I would like does A B C mean the intersection type of A, B and C. That is a supertype of all three roles. In addition we might need negation to get what Jonathan Lang envisoned for the Complex type that does Num !Comparable. IOW, I'm opting for a role combination syntax by means of logical operators that operate on the intension set of the involved roles. Could we get that? And while we're at it, could we also introduce the subtype operator : and perhaps : as the supertype operator? This would come in handy for expressing type constraints in does clauses. --
Re: class interface of roles
Ruud H.G. van Tol wrote: Larry Wall schreef: I suspect ordered composition is going to be rare enough that we can simply dehuffmanize it to $x does A; $x does B; $x does C; Maybe use a list-like notation? What happens when you try to mix ordered and unordered composition? Under the current syntax, to compose roles A and B together, and then C once A and B are done, one could say: $x does A | B does C; Under my original suggestion, this would be: $x does A does B does C; Under Larry's suggestion, this would be: $x does A does B; $x does C; How would you handle it? If ordered composition is something that can be done in a single statement, then unordered composition syntax needs to be nestable inside ordered composition syntax - and since the goal here is to have does A does B always represent unordered composition, that's what would need to nest. -- Despite having suggested a possible syntax for ordered composition, I think that Larry is right in claiming that a sequence of separate composition statements is more than sufficient in both syntax and clarity to handle those cases where ordered composition is needed. -- Jonathan Dataweaver Lang
Re: class interface of roles
TSa wrote: And while we're at it, could we also introduce the subtype operator : and perhaps : as the supertype operator? This would come in handy for expressing type constraints in does clauses. Isn't one of those called .does()? -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO, Hmm, no one seems to read the article! There actually is another class GenLocSquare that combines GenSquare and GenPointMixin. With that we get a modified version of my code as follows: role GenEqual { method equal( : GenEqual $ -- Bool ) {...} } role GenPointMixin { has Int $.x; has Int $.y; method equal( ::?CLASS GenEqual $self: ::?CLASS $p -- Bool ) This additional GenEqual type bound on the self type is all that is needed to get the superclass interface as described in the article. { return super.equal(p) and # -- handwave return call($p) and # normal superclass call, but I still # think that super.equal reads better. self.x == $p.x and self.y == $p.y; } } class GenSquare does GenEqual does GenPointMixin class GenSquare does GenEqual { has Int $.side; method equal ( : ::?CLASS $p -- Bool ) { return self.side == $p.side; } } And finally the combined class class GenLocSquare is GenSquare does GenPointMixin {} I initially dropped it because I thought that roles can see the pre-composed class somehow. But as Jonathan explained they don't---at least not in compile-time class composition. And for runtime composition you get the empty class for free! Regards, TSa. --
Re: class interface of roles
Jonathan Lang schreef: role R does A does B does C { ... } # unordered composition $x does A does B does C; # ordered composition $y does A | B | C; # unordered composition I'd like to see it done something like: role R does A does B does C { ... } # unordered composition $x does A does B does C; # ordered composition $y does A does B does C; # unordered composition And I was on the line of: role R does A | B | C { ... } # unordered composition $x does ( A, B, C ) ; # ordered composition $y does A | B | C ; # unordered composition but I would like early and late binding to be explicit. Maybe the coder will use () around the run-time ones. -- Affijn, Ruud Gewoon is een tijger.
Re: class interface of roles
Larry Wall wrote: Though actually, now that I think about it, the cascaded notation in S12 is illegal according to S03, since does is classified as non-chaining, which implies non-associative. Wait a minute. Isn't chaining specifically referring to the idea that A op B op C implicitly becomes something like A op B B op C? That would seem to be unrelated to the concept of associativity, other than the fact that you can't have chaining without associativity. Why would does have to be chaining in order to be associative? -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO Jonathan, you wrote: I think I found the core of the issue here; it has to do with the differences between roles and mixins, with an analogous difference between compile-time composition and runtime composition. Details follow. I think we are basically through the discussion. Thank you for your patience so far. It's interesting to note that you make a distinction between mixins and roles. I think of roles as being mixins. Ah. Unfortunately, said article is part 15 of a series. While it's true that only a handful of them are directly referenced by the article, that's still more than I've had time to read so far - and the unreferenced articles still matter in that they provide the grammatical context in which Article 15 is phrased. The series is worth reading, indeed. Just to clarify some terminology: in the above statement, 'ComposedClass' composes 'Role', and 'ComposedClass' inherits from 'UncomposedClass'. Conversely, 'Role' is composed into 'ComposedClass'. This is still not how I think of it. I'm more thinking of the composition process as having the *same* class in a pre- and a post-composition state. The role poses a type constraint on the pre-state and can access this state. There's no inheritance between two classes. As you describe it, the current compile-time composition let's the role define a type bound of the composition result in the sense that instances of the class do the role. F-bounded polymorphism? See e.g. http://www.cs.utexas.edu/~wcook/papers/FBound89/CookFBound89.pdf http://www.cs.washington.edu/research/projects/cecil/www/Vortex-Three-Zero/doc-cecil-lang/cecil-spec-86.html The basic idea is to define the type T of instances of a class or role as all types that are subtypes of a type function: forall T : Generator[T] The crux is that T appears on both sides of the typing relation. I would like to find out how it can be applied to Perl 6. So if you find the time to read through the series we can try together to understand the type system of Perl 6. Free mixins just go into the composed class unconstraint. Do you mean unconstrained? Yes, sorry for the typo. Regards, TSa. --
Re: class interface of roles
TSa wrote: you wrote: I think I found the core of the issue here; it has to do with the differences between roles and mixins, with an analogous difference between compile-time composition and runtime composition. Details follow. I think we are basically through the discussion. Thank you for your patience so far. It's interesting to note that you make a distinction between mixins and roles. I think of roles as being mixins. I'm only making the distinction based on a very restrictive definition of mixin - and I'm doing so largely to illustrate the conceptual differences between compile-time and runtime composition. Just to clarify some terminology: in the above statement, 'ComposedClass' composes 'Role', and 'ComposedClass' inherits from 'UncomposedClass'. Conversely, 'Role' is composed into 'ComposedClass'. This is still not how I think of it. I'm more thinking of the composition process as having the *same* class in a pre- and a post-composition state. I see it now. I still don't agree with using it as the default approach; but I see it. As you describe it, the current compile-time composition lets the role define a type bound of the composition result in the sense that instances of the class do the role. Note that because perl 6 uses a prototype approach to object-orientation, the class itself counts as an instance of the class. -- Jonathan Dataweaver Lang
Re: class interface of roles
On Tue, Oct 17, 2006 at 04:22:59PM -0700, Jonathan Lang wrote: : I've never really been happy with the inconsistency between runtime : composition and compile-time composition; but my problem has generally : been with the runtime side of things - in particular, I find the fact : that does A does B does C has an ordering at runtime to be annoying. : I understand the need for ordered composition; without it, there : would be no reliable way to resolve disputes between roles without : falling back to the original class (which isn't an option when the : goal is to change that aspect of the class' behavior). : : Personally, I would rather see does A does B does C be unordered in : all cases, and use something like does A does B does C to : represent ordered composition (where '' hints at its delayed : evaluation behavior in other situations). Or maybe use also, in : analogy to how a class can be defined in stages during compile-time; : but '' is probably better Huffman coding for this. You've got it inside out. Unordered is just does A | B | C or some such, the | there really being coerced to a set construction, not a junction. In fact, would work just as well. I only used | because it's more readable. Autocoercion of junctions to sets is, of course, conjectural. You really have does set(A,B,C) The | notation is mentioned in S012:1029, by the way. Obviously you still haven't quite memorized all the synopses. :-) Larry
Re: class interface of roles
Larry Wall wrote: You've got it inside out. Unordered is just does A | B | C or some such, the | there really being coerced to a set construction, not a junction. In fact, would work just as well. I only used | because it's more readable. Autocoercion of junctions to sets is, of course, conjectural. You really have does set(A,B,C) I wasn't aware that you were using set semantics there; but other than that clarification, I knew exactly what Unordered is - see below. The | notation is mentioned in S012:1029, by the way. Obviously you still haven't quite memorized all the synopses. :-) Actually, I was very well aware of that fact. The above was a proposal for changing S12, for the express purpose of making the syntax for unordered composition at run-time look like the syntax for unordered composition at compile-time as much as possible (by the principle of least surprise). Since this would remove the current syntax for doing ordered composition at runtime, and given that ordered composition at runtime is not something we want to lose, I was also proposing a replacement syntax for it. So instead of role R does A does B does C { ... } # unordered composition $x does A does B does C; # ordered composition $y does A | B | C; # unordered composition I'd like to see it done something like: role R does A does B does C { ... } # unordered composition $x does A does B does C; # ordered composition $y does A does B does C; # unordered composition Same capabilities, but less cognitive dissonance. Mind you, I don't really care what syntax gets used for ordered composition; just as long as it isn't does A does B does C. -- Jonathan Dataweaver Lang
Re: class interface of roles
Though actually, now that I think about it, the cascaded notation in S12 is illegal according to S03, since does is classified as non-chaining, which implies non-associative. Hmm. We'd have to make it right associative somehow. Put it in with ** maybe? Hmm. Or leave it non-associative and force | or ; notation to keep things crystal clear. The basic problem is that the declarational does has a natural scope that it applies to, so it doesn't have to arbitrarily limit its scope as a binary operator does. Larry
Re: class interface of roles
On Wed, Oct 18, 2006 at 01:32:14PM -0700, Jonathan Lang wrote: : The | notation is mentioned in S012:1029, by the way. Obviously you : still haven't quite memorized all the synopses. :-) : : Actually, I was very well aware of that fact. Oops, didn't realize you were suggesting a semantic change to the default, not just a syntactic addition. : The above was a : proposal for changing S12, for the express purpose of making the : syntax for unordered composition at run-time look like the syntax for : unordered composition at compile-time as much as possible (by the : principle of least surprise). Since this would remove the current : syntax for doing ordered composition at runtime, and given that : ordered composition at runtime is not something we want to lose, I was : also proposing a replacement syntax for it. So instead of : :role R does A does B does C { ... } # unordered composition :$x does A does B does C; # ordered composition :$y does A | B | C; # unordered composition : : I'd like to see it done something like: : :role R does A does B does C { ... } # unordered composition :$x does A does B does C; # ordered composition :$y does A does B does C; # unordered composition : : Same capabilities, but less cognitive dissonance. Gotcha. Sorry my brain was in sidewayser than usual. : Mind you, I don't really care what syntax gets used for ordered : composition; just as long as it isn't does A does B does C. I suspect ordered composition is going to be rare enough that we can simply dehuffmanize it to $x does A; $x does B; $x does C; Larry
Re: class interface of roles
Larry Wall wrote: Though actually, now that I think about it, the cascaded notation in S12 is illegal according to S03, since does is classified as non-chaining, which implies non-associative. Hmm. We'd have to make it right associative somehow. Put it in with ** maybe? Hmm. Or leave it non-associative and force | or ; notation to keep things crystal clear. The basic problem is that the declarational does has a natural scope that it applies to, so it doesn't have to arbitrarily limit its scope as a binary operator does. Personally, I'd rather it be right-associative: if class Foo is Bar does A does B { ... } works, I'd expect $x does A does B; to work, too. What trouble would we get into by moving does to the chaining binaries priority (i.e., one step more restrictive than it currently is)? -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO Jonathan, you wrote: Of course, you then run into a problem if the class _doesn't_ redefine method equal; if it doesn't, then what is GenPointMixin::equal calling? This is the reason why there is a type bound on the class that should result in a composition error when the equal method is missing. Shouldn't the 'divert' be a trait of the method instead of a key/value pair on the class? And what does your syntax mean? Looks like the key is indicating the method and the value is the namespace where method lookup starts. And you also run into a problem if you want the class to track the position in polar coordinates instead of rectilinear ones - it would still represent a point conceptually, but the implementation of method equal (at least) would need to be overridden. Oh, yes. Doing the right thing is difficult. Changing representation of the point while keeping the interface can only be done in the class. OTOH, the interface would include the rectilinear accessor methods. And these are called in the equal method. Thus a class doing the GenPoint role correctly needs to provide .x and .y methods even if they aren't simple auto-generated accessors of attributes. But note that these two are not going through the superclass interface but the self type. In the case of the equal method the dispatch slot contains the role's closure which calls into the class' closure. There is no dispatch to the role because the role is flattened out in the composition process. It is interesting to think of another PolarPoint role that also has an equal method that would conflict with the GenPoint one. Then the class has to disambiguate. Which in turn requires the class to provide an equal method that overrides both role versions. Note that I think the conflict detection of role methods prevents the composition of the equal method through the superclass interface. I admit that this warps the meaning of the class' equal method from beeing an aspect in the role's method to the definer of the method. This can be a source of subtle bugs. That is the class composer can't distinguish an aspect method from a disambiguation one unless we introduce e.g. an 'is disambig' trait. And e.g. an 'is override' trait when the class designer wishes to replace a role method even if there's no conflict. A cleaner solution would be to define a private helper method which the role then demands that the class override. This is _very_ similar to the solution that you described as clumsy a few posts back, with the main difference being that the helper method, being private, can't be called outside of the class. To me, the only clumsiness of this solution comes directly from the cluminess of the overall example, and I consider your proposed alternative to be equally clumsy - it merely trades one set of problems for another. There's a lot of truth in that. But I don't consider the example as clumsy. It is an important issue that arises whenever method recombination is needed to deal with the guarantees that the role as a type makes. Calculation of a type bound on the class that has to be met in the composition is a strong tool. And yes, in Perl 6 the method isn't called equal but eqv or === and has a default implementation that retrieves the .WHICH of both args. What inspired that comment? Sorry, I didn't want to intimidate you. But I wanted to prevent comments that choosing a method equal is not the right approach and MMD should be used instead. But I think all of what we discussed so far stays valid if the equal method is a multi. IIRC there is just one namespace slot for the short name. And we are discussing how this slot is filled in the class composition process. BTW, why have you gone off-list? Regards, TSa. --
Re: class interface of roles
TSa wrote: Jonathan Lang wrote: Of course, you then run into a problem if the class _doesn't_ redefine method equal; if it doesn't, then what is GenPointMixin::equal calling? This is the reason why there is a type bound on the class that should result in a composition error when the equal method is missing. But it isn't missing; it's being inherited from the role. Shouldn't the 'divert' be a trait of the method instead of a key/value pair on the class? I thought about doing it that way; but then the class wouldn't know to look for it when composing the role. And what does your syntax mean? Looks like the key is indicating the method and the value is the namespace where method lookup starts. That was the idea. And you also run into a problem if you want the class to track the position in polar coordinates instead of rectilinear ones - it would still represent a point conceptually, but the implementation of method equal (at least) would need to be overridden. Oh, yes. Doing the right thing is difficult. Changing representation of the point while keeping the interface can only be done in the class. OTOH, the interface would include the rectilinear accessor methods. And these are called in the equal method. ...unless the equal method is rewritten so that it doesn't call them. That said, you're right that the rectlinear access methods will still be there - but they won't neccessarily work the same way. In particular, I can see .x being rewritten to return $.r * cos($.a) when used to retrieve the value. Internally, the representation would be in polar notation; externally, rectilinear accessors could still be used - though they wouldn't be as efficient as polar accessors. In the case of the equal method the dispatch slot contains the role's closure which calls into the class' closure. There is no dispatch to the role because the role is flattened out in the composition process. Conceptually, yes; you can think of it in that way. In practice, this is implemented by having the class dispatch to the role if none of the class' signatures match the call. Bear in mind, though, that _every_ call follows this route: even a method call that originates in a role's method (such as an attribute accessor) will start the dispatch process with the class, then move to the roles if the class doesn't work, and then move up the class hierarchy if the roles don't work. It is interesting to think of another PolarPoint role that also has an equal method that would conflict with the GenPoint one. Then the class has to disambiguate. Which in turn requires the class to provide an equal method that overrides both role versions. Agreed. Note that I think the conflict detection of role methods prevents the composition of the equal method through the superclass interface. Just to make sure we're speaking the same language: by superclass, you're referring to classes brought in via is; right? If so, you're correct: the presence of a valid method in any of the roles that a class composes is sufficient to prevent the dispatch process from ever reaching any of the superclasses. I admit that this warps the meaning of the class' equal method from being an aspect in the role's method to the definer of the method. This can be a source of subtle bugs. That is the class composer can't distinguish an aspect method from a disambiguation one unless we introduce e.g. an 'is disambig' trait. And e.g. an 'is override' trait when the class designer wishes to replace a role method even if there's no conflict. ...and you would then have to apply the 'override' trait to every class method that is used instead of the role method. Note that as written, this is the default approach, and you only have to do something exotic in the (comparatively) rare situation where you want the class to complete the role's method instead of replacing it. What inspired that comment? Sorry, I didn't want to intimidate you. I wasn't intimidated; just curious. -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO, Jonathan Lang wrote: TSa wrote: Note that I think the conflict detection of role methods prevents the composition of the equal method through the superclass interface. Just to make sure we're speaking the same language: by superclass, you're referring to classes brought in via is; right? No. I used the term 'superclass interface' as it appears in the article I referred to. There the role has a type constraint interface to the uncomposed class which is available through the super keyword. Let's settle for the terms 'uncomposed class' and 'composed class'. Then I meant the interface of the role to the uncomposed class. It might be the case that you should think of the role composition to happen on a new anonymous class that inherits everything from the uncomposed class and therefore has a proper superclass interface. Conflicts between roles prevent the installation of a composed method and hence leave the task of defining it to the composed class. I admit that the distinction between these cases can come as a surprise to class developers. But composing more than one role is equal in complexity as multiple inheritance. So a warning is a good thing here. The 'is override' trait might be needed to silence the warning. Well, and you can switch it off with a pragma or the trait on the class. The whole point of the article is to calculate the composed class type. This process is akin to inheritance but needs two implicit type parameters to model it in F-bounded polymorphism. Free mixins just go into the composed class unconstraint. Constraint mixins require certain features from the class. This inevitably shifts the balance between roles and classes towards roles which is OK in my eyes. In particular since roles have taken on the meaning of type in Perl 6 and as such make the stronger claims when used where a type is expected. If so, you're correct: the presence of a valid method in any of the roles that a class composes is sufficient to prevent the dispatch process from ever reaching any of the superclasses. I'm more thinking along the lines of closure composition and a flattened view of the class. That is conceptually the class' namespace directly maps slot names to methods. That this mapping is searching the inheritance graph behind the scenes is an implementation detail. In the case of roles the composed method calls the uncomposed one that's all. In your terms this means 'dispatch starts at the role' I guess. On a side track I want to note: the optimizer might munge all combined methods into an optimized version that doesn't walk the class graph. In the GenSquare case this method reads return self.side == $p.side and self.x == $p.x and self.y == $p.y and is installed in the equal slot of the composed class. I'm interpreting roles from a type theoretical background. This is viable because all information needed is available at compile time whereas classes are open. Note that the combination process as I see it nicely explains runtime role composition in the same way as compile time role composition. There is no special rule that at runtime the role takes precedence or forces its definition of methods. Also the difference between successive does and a precomposed role with | falls out naturally. In the latter case conflicts let role methods drop out and leave the class method as disambiguation. But the compiler can easily issue a warning for that. Regards, TSa. --
Re: class interface of roles
HaloO, Jonathan Lang wrote: Shouldn't the 'divert' be a trait of the method instead of a key/value pair on the class? I thought about doing it that way; but then the class wouldn't know to look for it when composing the role. I figure you see the class in a very active role when composing roles. I see them more as a passive entity that the class composer is dealing with. My scope is on producing a predictable result from the typing point of view. Dispatch comes later. Regards, TSa. --
Re: class interface of roles
I think I found the core of the issue here; it has to do with the differences between roles and mixins, with an analogous difference between compile-time composition and runtime composition. Details follow. TSa wrote: Jonathan Lang wrote: Just to make sure we're speaking the same language: by superclass, you're referring to classes brought in via is; right? No. I used the term 'superclass interface' as it appears in the article I referred to. There the role has a type constraint interface to the uncomposed class which is available through the super keyword. Ah. Unfortunately, said article is part 15 of a series. While it's true that only a handful of them are directly referenced by the article, that's still more than I've had time to read so far - and the unreferenced articles still matter in that they provide the grammatical context in which Article 15 is phrased. Let's settle for the terms 'uncomposed class' and 'composed class'. Then I meant the interface of the role to the uncomposed class. Let me figure out what you mean by 'uncomposed class' and 'composed class' before I agree to the terminology. The original article seems to be making an assumption that you will always take an existing class and a mixin in order to create a new class. If this is the assumption, then the existing class would be the 'uncomposed class', the mixin would be the 'role', and the new class would be the 'composed class'. This, however, is not the default paradigm used by Perl 6. If you want to do this sort of thing in perl 6, you say something like: class ComposedClass is UncomposedClass does Role { ... } In particular, perl 6 does not assume the existence of an uncomposed class when using roles unless you explicitly specify one: there is just the role and the composed class that fleshes out that role. This, I believe, is the fundamental difference between roles and mixins: the latter assumes that it's being mixed in to something pre-existing to produce something new; the former doesn't. Just to clarify some terminology: in the above statement, 'ComposedClass' composes 'Role', and 'ComposedClass' inherits from 'UncomposedClass'. Conversely, 'Role' is composed into 'ComposedClass'. 'UncomposedClass' is a superclass of 'ComposedClass', 'ComposedClass' is a subclass of 'UncomposedClass', and 'Role' is a role of 'ComposedClass'. AFAIK, there's no standard term for what 'ComposedClass' is in relation to 'Role' - though personally, I'd tend to say that 'ComposedClass' is an implementation of 'Role'. My understanding is that mixin terminology would be that 'role' is mixed into 'UncomposedClass' to produce 'ComposedClass'. It might be the case that you should think of the role composition to happen on a new anonymous class that inherits everything from the uncomposed class and therefore has a proper superclass interface. The only situation in perl6 that I know of where an anonymous class is generated when a role is composed is when you compose the role at runtime. Incidently, your comments to date are much more compatable with runtime composition than they are with compile-time composition. In fact, a case could be made that runtime composition always uses the mixin paradigm, whereas compile-time composition doesn't. The paradigm used by compile-time composition is that the role acts as a blueprint for building the class. It does not get mixed in to anything; it instead provides requirements that the class must meet, and an optional toolkit that the class may use in meeting those requirements. Conflicts between roles prevent the installation of a composed method and hence leave the task of defining it to the composed class. This is how things work now, during compile-time: if two roles provide potentially incompatable method implementations, the class is required to supply a replacement for them. If I understand runtime composition correctly, undefined methods in a role fall back on the uncomposed class' definitions; this means that it is an error to compose a role at runtime that declares a method that the uncomposed class lacks without defining it, even if a later role in the sequence provides a definition for it. I admit that the distinction between these cases can come as a surprise to class developers. But composing more than one role is equal in complexity as multiple inheritance. So a warning is a good thing here. The 'is override' trait might be needed to silence the warning. Well, and you can switch it off with a pragma or the trait on the class. The distinction _does_ come as a surprise to me, but only because perl handles single-role composition in a way that's fully compatable with the multiple-role composition strategy that you just outlined. That is, perl doesn't make a distinction; it assumes single-role composition to be a special case of multiple-role composition. The whole point of the article is to calculate the composed class type. This process is akin to
Re: class interface of roles
HaloO, Jonathan Lang wrote: class GenSquare does GenPoint does GenEqual { has Int $.side; method equal ( GenSquare $p -- Bool ) { return $self.GenPoint::equal($p) and $self.side == $p.side; } } This is exactly what I don't want. Such an equal method needs to be written in each and every class the role GenPoint is composed into. This is what I call a burden instead of code reuse by means of role composition. But I guess our points of view are hard to reconcile. You and the current spec see the class having the final word whereas I think that the role takes over responsibility. This does not require a telepathic compiler. Simply giving priority to the role suffices. Having the role in charge nicely matches the fact that the guarantee of doing a role makes a stronger claim than being an instance of a class. Doing the role happens with objects from different classes. And now imagine that some classes implemented equality correctly and some don't. With my approach the chance for this is diminished to a correct class specific implementation that is required and used by the role. Regards, TSa. --
Re: class interface of roles
HaloO, Jonathan Lang wrote: Still not following. Can you give an example? The example in the original post contains a class GenSquare that has got an equal method that checks the sides of the self square and the incoming argument square. The GenPointMixin role provides an equal method that compares the x and y coordinates. The correct final method equal of the composed class needs to check sides and coordinates. If the simple side checking equal of the class overrides the closure from the role there is a false implementation. OTOH, forcing all classes to call the role closure is bad design of the composition process. Hence I'm arguing for a super keyword that in a role refers to the class. In the example super.equal($p) calls the side checking closure. Note that it's entirely possible for attributes to not make it into the final class, if the accessor methods get redefined in such a way as to remove reference to the attributes. This is part of the notion that roles supply an outline of what the class should do, but only the class actually supplies the definitive details of how to do it. I see that you regard the class as the ultimate definer of functionality. The role requires a method including its signature but the class implements it. For the type system the role encodes the guarantee of the method availability. As I see it: is and does declarations in a role impose requirements on the final class: it must derive from another class (is), or it must compose another role (does). method and has are each one part requirement and one part suggestion: for method, the class is required to include a method that matches the given name (which, of course, includes the method's signature), and a particular closure is suggested that the class can accept or override. I almost agree here. The only thing I'm asking for is that the role's closure is not discarded so easily. The designer of the role takes responsibility for the role's part in the final method closure. The combination process should produce the correct result automatically and independent of the class's cooperation. Thus, the only things that I'd recommend using to calculate a type-boundary would be the superclasses (provided by is) and the method names (provided by method and has). That makes classes too typeish. The type system is mostly based on roles. The class inheritance graph should not be used for typing. Or do you want constraints on the class derivation process that guarantees subclasses to be subtypes? As I read it the class derivation is free to violate replaceability of subclasses where superclasses are expected. Roles are a guarantee of functionality not classes. I agree with the idea behind the current definition of this: if the class provides its own definition for a method, that should take precedence over the role's definition. If it doesn't, then it adopts the role's definition as its own. I would hope it is the role if a as of now unknown syntax has declared it. Perhaps it should be even the default. The rational for my claim is that a role is composed several times and then every class doing the role automatically gets the correct version. Otherwise all classes are burdened with caring for the role's part in the method. Huh? Yeah! The role adds a certain aspect to the correct implementation of a method. And so does the class. But it is the role that is composed into the class not the other way around. A role is intended to be composed several times into completely different classes. With blind precedence to class methods the role's aspects are lost and have to be reintroduced in each and every class. I consider that inconvenient and error prone. It should assume that if Foo overrides A's implementation of blah, Foo knows what it's doing; by the principle of least surprise, Foo should never end up overriding A's implementation of blah only to find that the original implementation is still being used by another of the methods acquired from A. Could you make an example because I don't understand what you mean with original implementation and how that would be used by role methods. role A { method foo() { say Ah... } method bar() { $self.foo() } } Isn't that self.foo() without the sigil? It is clear that .foo is dispatched on the class. [..] class Baz { method foo() { $self!'A::foo'(); say Choo!; } method bar() { $self.foo() } method baz() { $self!'A::foo'() } my method 'A::foo'() { say Ah... } } What is A referring to here? Baz doesn't compose role A here. And why the exclamation mark? In Baz, Cmy method 'A::foo'() represents the original implementation of foo(), while Cmethod foo() represents the final implementation chosen by the class. OK, thanks for the example. If you were to allow a role method to directly refer to its own implementation, you could do something like: role A { method foo() { say Ah... }
Re: class interface of roles
TSa wrote: HaloO, Jonathan Lang wrote: Still not following. Can you give an example? The example in the original post contains a class GenSquare that has got an equal method that checks the sides of the self square and the incoming argument square. The GenPointMixin role provides an equal method that compares the x and y coordinates. The correct final method equal of the composed class needs to check sides and coordinates. If the simple side checking equal of the class overrides the closure from the role there is a false implementation. Actually, the correct equal method for the composed class needs to check the sides of the square, because how the class defines it: in this example, class GenSquare _is_ the final class, and so whatever method it provides _is_ the final method, for good or ill. If this results in other method definitions inherited from GenPointMixin behaving strangely, then GenSquare needs to override those methods as well. Mind you, the above implies a radical shift in the underlying semantics, akin to having class Dog compose role Tree and redefining method bark() to suit its own nature. Proper use of role composition is to refine the role's underlying concept, not to radically alter it. Even if we were to go with a modified version of the original example, where GenSquare is a role and both GenSquare and GenPoint are being composed into a final class - say, GenPositionedSquare - you still have the problem that the final class is conceptually supposed to be both a GenSquare and a GenPoint at the same time. If the underlying concepts behind the composed roles are fundamentally incompatable (such as a Dog and a Tree), you're going to have problems. OTOH, forcing all classes to call the role closure is bad design of the composition process. Hence I'm arguing for a super keyword that in a role refers to the class. In the example super.equal($p) calls the side checking closure. This is what happens by default; no special syntax is needed. Note that it's entirely possible for attributes to not make it into the final class, if the accessor methods get redefined in such a way as to remove reference to the attributes. This is part of the notion that roles supply an outline of what the class should do, but only the class actually supplies the definitive details of how to do it. I see that you regard the class as the ultimate definer of functionality. The role requires a method including its signature but the class implements it. For the type system the role encodes the guarantee of the method availability. Exactly. As I see it: is and does declarations in a role impose requirements on the final class: it must derive from another class (is), or it must compose another role (does). method and has are each one part requirement and one part suggestion: for method, the class is required to include a method that matches the given name (which, of course, includes the method's signature), and a particular closure is suggested that the class can accept or override. I almost agree here. The only thing I'm asking for is that the role's closure is not discarded so easily. The designer of the role takes responsibility for the role's part in the final method closure. The combination process should produce the correct result automatically and independent of the class's cooperation. Perl has yet to implement a telepathic compiler, so the compiler can only make best guesses as to what the programmer intended. For now, we have to make some concessions to reality; one such concession is that the most specific thing should have authority over (and responsibility for) the less specific things. The thing being composed into is by definition more specific than the thing being composed when it comes to implementation; thus, the latter should be given free rein to redefine the methods provided by the former - and if things go wrong, you should blame the latter rather than the former. Letting a role override a decision made by a class that it's composed into means that there's something about the role's implementation that the class cannot touch. Thus, the only things that I'd recommend using to calculate a type-boundary would be the superclasses (provided by is) and the method names (provided by method and has). That makes classes too typeish. The type system is mostly based on roles. The class inheritance graph should not be used for typing. It is a fact that class inheritance affects the behaviour of a class; it cannot be disregarded when considering type. That said, class inheritance (with respect to its semantic differences from role composition) is an area where my understanding is rather shaky. To me, the whole notion of inheritance is a bit too typeish for my tastes; I try to handle _all_ code reuse by means of roles, with classes only being brought into play when I want to be able to instantiate an object. That is, I tend to define what an object is
Re: class interface of roles
HaloO, Jonathan Lang wrote: What do you mean by uncomposed class? The self always refers to the object as instance of the composed class. Methods are therefore resolving to the outcome of the composition process. But super in a role refers to methods from the class definition even when the final method comes from method combination in the composition process. I think the word super is already to overloaded to be used for this purpose. Setting that aside for now... Better ideas? Is there a super keyword already? Or do you mean overloaded in general OO and type speak? Do you mean that super.blah() appearing in a method defined in a role A should require that all classes which do A need to provide a blah implementation? (or produce a composition time error if they don't) Yes, this is exactly what I mean with superclass interface of roles. I hope not; that's exactly what declaring an unimplemented method in a role is supposed to do. My idea is that the type system calculates a type bound for the class from the definition of the role. That includes attributes and their accessor methods. It's a matter of taste how explicit this interface is declared or how much of it is infered. (And declaring an implemented method does the same thing, with the addition that it also suggests an implementation that the class is free to use or ignore as it sees fit.) We have a priority conflict here. The question is if the class or the role is seeing the other's methods for method combination. A Role should be able to say to do this Role you need to implement these methods and have a compile/composition time error if not. Agreed. I agree as well. But on a wider scope then just method provision. (There does need to be a way to call, in a Role A, both the blah defined in A and whatever the blah the final class may use. Yes, this is the subject of the current debate. I'm opting for a method combination semantics that allows the role to call the class method. $self.blah() is the later, $self.A::blah() or similar is likely to be the former.) No, there doesn't. Given that Cclass Foo does A and Crole A does B, There needs to be a way to call, in class Foo, both the blah defined in Foo, the blah defined in A (so that Foo can reimplement A's version as a different method or as part of its own), and the blah defined in B; From the class all composed parts are available through namespace qualified names. But a role is a classless and instanceless entity. The self refers to the objects created from the composed class. The role is not relevant in method dispatch. That is a method is never dispatched to a role. But the role should be able to participate in the method definition of the composed class. and there needs to be a way to call, in role A, both the blah defined in Foo and the blah defined B; but role A does not need a way to explicitly call a method defined in A. I'm not sure if I get this right. But as I said above a role can not be dispatched to. Which method do you think should take precedence the role's or the class's? That is who is the defining entity in the method combination process? I would hope it is the role if a as of now unknown syntax has declared it. Perhaps it should be even the default. The rational for my claim is that a role is composed several times and then every class doing the role automatically gets the correct version. Otherwise all classes are burdened with caring for the role's part in the method. It should assume that if Foo overrides A's implementation of blah, Foo knows what it's doing; by the principle of least surprise, Foo should never end up overriding A's implementation of blah only to find that the original implementation is still being used by another of the methods acquired from A. Could you make an example because I don't understand what you mean with original implementation and how that would be used by role methods. Method dispatch is on the class never on the role. As far as dispatch is concerned the role is flattend out. But the question is how the class's method is composed in the first place. Regards, --
Re: class interface of roles
HaloO, Jonathan Lang wrote: So if I'm reading this right, a class that does both A and B should be lower in the partial ordering than a class that does just one or the other. And if A does B, then you'll never have a class that does just A without also doing B, which trims out a few possible nodes and paths from the lattice for practical purposes: Any={} | \ | \ |\ | \ | \ B={y} C={z} / \ | / \ | / \| / \ | / \ | / \ | A|B={x,y} B|C={y,z} \ / \ / \ / \ / \ / A|B|C={x,y,z} Correct. The lattice is a structural analysis of the roles. I note that while the lattice is related to whatever role hierarchies may or may not exist, it is not the same as them. In particular, roles that have no hierarchal relationship to each other _will_ exist in the same lattice. In fact, all roles will exist in the same lattice, on the first row under Any. Right? Yes, if they are disjoined structurally. Otherwise intersection roles appear as nodes under Any. Or does the fact that A does B mean that A would be placed where A|B is, and A|C would end up in the same node as A|B|C? To get at the node labeled A|B above you either need a definition role A does B { has $.x } or an outright full definition role A { has $.x; has $.y } So, yes the node should be called A and A|C coincides with A|B|C. I'm not sure if this ordering of roles can be called duck typing because it would put roles that have the same content into the same lattice node. The well known bark method of Dog and Tree comes to mind. But the arrow types of the methods will be different. One has type :(Dog -- Dog) the other :(Tree -- Tree) and a joined node will have type :(DogTree -- Dog|Tree). This might just give enough information to resolve the issues surrounding the DogTree class. By most specific, you'd mean closest to the top? No, closer to the bottom. The join operator | of the lattice produces subtypes with a larger interface that is more specific. It's like the more derived class in a class hierarchy. Regards, TSa. --
Re: class interface of roles
TSa wrote: Jonathan Lang wrote: What do you mean by uncomposed class? The self always refers to the object as instance of the composed class. Methods are therefore resolving to the outcome of the composition process. But super in a role refers to methods from the class definition even when the final method comes from method combination in the composition process. Still not following. Can you give an example? I hope not; that's exactly what declaring an unimplemented method in a role is supposed to do. My idea is that the type system calculates a type bound for the class from the definition of the role. That includes attributes and their accessor methods. It's a matter of taste how explicit this interface is declared or how much of it is inferred. Note that it's entirely possible for attributes to not make it into the final class, if the accessor methods get redefined in such a way as to remove reference to the attributes. This is part of the notion that roles supply an outline of what the class should do, but only the class actually supplies the definitive details of how to do it. As I see it: is and does declarations in a role impose requirements on the final class: it must derive from another class (is), or it must compose another role (does). method and has are each one part requirement and one part suggestion: for method, the class is required to include a method that matches the given name (which, of course, includes the method's signature), and a particular closure is suggested that the class can accept or override. For has, the class is required to provide accessor methods corresponding to the attribute's read/write capabilities (a rw method if it's a rw attribute; a regular method if it's a regular attribute; and no method if it's a private attribute); like any other method, a closure is suggested that the class may accept or override. In addition, the role suggests that a given attribute be added to the class' state information. This suggestion is implicitly accepted if any of the methods that are used by the final class refer to the attribute; it is implicitly rejected if none of them do. The same rule applies to private methods. Thus, the only things that I'd recommend using to calculate a type-boundary would be the superclasses (provided by is) and the method names (provided by method and has). (And declaring an implemented method does the same thing, with the addition that it also suggests an implementation that the class is free to use or ignore as it sees fit.) We have a priority conflict here. The question is if the class or the role is seeing the other's methods for method combination. I believe that I address this later on; if not, please clarify. (There does need to be a way to call, in a Role A, both the blah defined in A and whatever the blah the final class may use. Yes, this is the subject of the current debate. I'm opting for a method combination semantics that allows the role to call the class method. Agreed. $self.blah() is the later, $self.A::blah() or similar is likely to be the former.) No, there doesn't. Given that Cclass Foo does A and Crole A does B, There needs to be a way to call, in class Foo, both the blah defined in Foo, the blah defined in A (so that Foo can reimplement A's version as a different method or as part of its own), and the blah defined in B; From the class all composed parts are available through namespace qualified names. But a role is a classless and instanceless entity. The self refers to the objects created from the composed class. The role is not relevant in method dispatch. That is a method is never dispatched to a role. But the role should be able to participate in the method definition of the composed class. Also agreed. In particular, I'm referring to _how_ a role should participate in the method definition of the composed class; I am not referring to method dispatch, which is limited to the class hierarchy. and there needs to be a way to call, in role A, both the blah defined in Foo and the blah defined B; but role A does not need a way to explicitly call a method defined in A. I'm not sure if I get this right. But as I said above a role can not be dispatched to. Which method do you think should take precedence the role's or the class's? That is who is the defining entity in the method combination process? I agree with the idea behind the current definition of this: if the class provides its own definition for a method, that should take precedence over the role's definition. If it doesn't, then it adopts the role's definition as its own. I would hope it is the role if a as of now unknown syntax has declared it. Perhaps it should be even the default. The rational for my claim is that a role is composed several times and then every class doing the role automatically gets the correct version. Otherwise all classes are burdened with caring for the role's part in the method. Huh?
Re: class interface of roles
TSa wrote: TSa wrote: Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. As such it is a useful guidance for error reports in the class composition process. Actually 'next METHOD' doesn't catch all superclass interface issues. There is the simple case of calling e.g. accessor methods on super which should result in the requirement to provide them. So I still propose a super keyword that in roles means the object as seen from the uncomposed class. I think the word super is already to overloaded to be used for this purpose. Setting that aside for now... Do you mean that super.blah() appearing in a method defined in a role A should require that all classes which do A need to provide a blah implementation? (or produce a composition time error if they don't) I'd prefer to have a declarative mechanism for specifying such requirements. Firstly for self-documenting clarity, there's no need to scan for a super, and secondly because eval and super.$method_name would allow runtime failures. Some alternatives appeared elsewhere in this thread but it's unclear whether they would produce error at composition time or at runtime. (yada methods) The same applies to the next METHOD inference suggested: Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. A Role should be able to say to do this Role you need to implement these methods and have a compile/composition time error if not. (There does need to be a way to call, in a Role A, both the blah defined in A and whatever the blah the final class may use. $self.blah() is the later, $self.A::blah() or similar is likely to be the former.) Brad -- When one is not capable of true intelligence, it is good to consult with someone of good sense. -- Hagakure http://bereft.net/hagakure/
Re: class interface of roles
Brad Bowman wrote: TSa wrote: TSa wrote: Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. As such it is a useful guidance for error reports in the class composition process. Actually 'next METHOD' doesn't catch all superclass interface issues. There is the simple case of calling e.g. accessor methods on super which should result in the requirement to provide them. So I still propose a super keyword that in roles means the object as seen from the uncomposed class. What do you mean by uncomposed class? I think the word super is already to overloaded to be used for this purpose. Setting that aside for now... Do you mean that super.blah() appearing in a method defined in a role A should require that all classes which do A need to provide a blah implementation? (or produce a composition time error if they don't) I hope not; that's exactly what declaring an unimplemented method in a role is supposed to do. (And declaring an implemented method does the same thing, with the addition that it also suggests an implementation that the class is free to use or ignore as it sees fit.) The same applies to the next METHOD inference suggested: Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. A Role should be able to say to do this Role you need to implement these methods and have a compile/composition time error if not. Agreed. (There does need to be a way to call, in a Role A, both the blah defined in A and whatever the blah the final class may use. $self.blah() is the later, $self.A::blah() or similar is likely to be the former.) No, there doesn't. Given that Cclass Foo does A and Crole A does B, There needs to be a way to call, in class Foo, both the blah defined in Foo, the blah defined in A (so that Foo can reimplement A's version as a different method or as part of its own), and the blah defined in B; and there needs to be a way to call, in role A, both the blah defined in Foo and the blah defined B; but role A does not need a way to explicitly call a method defined in A. It should assume that if Foo overrides A's implementation of blah, Foo knows what it's doing; by the principle of least surprise, Foo should never end up overriding A's implementation of blah only to find that the original implementation is still being used by another of the methods acquired from A. -- Jonathan Dataweaver Lang
Re: class interface of roles
TSa wrote: Jonathan Lang wrote: TSa wrote: Dispatch depends on a partial ordering of roles. Could someone please give me an example to illustrate what is meant by partial ordering here? In addition to Matt Fowles explanation I would like to give the following example lattice build from the roles role A { has $.x } role B { has $.y } role C { has $.z } There can be the four union combined roles A|B, A|C, B|C and A|B|C which complete the type lattice: Any={} / | \ / | \ /|\ / | \ / | \ A={x} B={y} C={z} | \/ \/ | | \ / \ / | | \/ \/ | | /\ /\ | | / \ / \ | | /\ /\ | A|B={x,y} A|C={x,z} B|C={y,z} \ | / \ | / \|/ \ | / \ | / A|B|C={x,y,z} So if I'm reading this right, a class that does both A and B should be lower in the partial ordering than a class that does just one or the other. And if A does B, then you'll never have a class that does just A without also doing B, which trims out a few possible nodes and paths from the lattice for practical purposes: Any={} | \ | \ |\ | \ | \ B={y} C={z} / \ | / \ | / \| / \ | / \ | / \ | A|B={x,y} B|C={y,z} \ / \ / \ / \ / \ / A|B|C={x,y,z} I note that while the lattice is related to whatever role hierarchies may or may not exist, it is not the same as them. In particular, roles that have no hierarchal relationship to each other _will_ exist in the same lattice. In fact, all roles will exist in the same lattice, on the first row under Any. Right? Or does the fact that A does B mean that A would be placed where A|B is, and A|C would end up in the same node as A|B|C? This lattice can then be used for type checks and specificity comparisons in dispatch. BTW, such a lattice can be calculated lazily from any set of packages. In pure MMD the selected target has to be the most specific in all dispatch relevant positions. By most specific, you'd mean closest to the top? -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO, Stevan Little wrote: I think that maybe we need to seperate the concept of roles as types and roles as partial classes, they seem to me to be in conflict with one another. And even they are not in conflict with one another, I worry they will bloat the complexity of roles usage. The bloat aside I believe it is essential to have roles as the key players of the type system. I propose to handle the typeish aspect of roles as described in the paper I linked to: there should be a trait 'is augmented' that is applicable to a role and to methods in a role. In such a method a call to next METHOD should invoke the class's method. Alternatively we could make the method combination behavior the default and have a class method trait 'is disambig' or 'is override' for the case where the class needs to have the final word. Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. As such it is a useful guidance for error reports in the class composition process. My experiences thus far with roles in Moose have been that they can be a really powerful means of reuse. I point you towards Yuval Kogman's latest work on Class::Workflow, which is basically a loose set of roles which can be composed into a highly customizable workflow system. This is where I see the real power of roles coming into play. Is this a set of free mixins or are they dependent on the class to provide a certain interface to fully achieve the means of the roles? I also consider roles a powerful tool but I believe that the type system should play a role in the composition process that goes beyond simple checking of name clashes. Regards, --
Re: class interface of roles
HaloO, Jonathan Lang wrote: TSa wrote: Dispatch depends on a partial ordering of roles. Could someone please give me an example to illustrate what is meant by partial ordering here? In addition to Matt Fowles explanation I would like to give the following example lattice build from the roles role A { has $.x } role B { has $.y } role C { has $.z } There can be the four union combined roles A|B, A|C, B|C and A|B|C which complete the type lattice: Any={} / | \ / | \ /|\ / | \ / | \ A={x} B={y} C={z} | \/ \/ | | \ / \ / | | \/ \/ | | /\ /\ | | / \ / \ | | /\ /\ | A|B={x,y} A|C={x,z} B|C={y,z} \ | / \ | / \|/ \ | / \ | / A|B|C={x,y,z} Note that A = (A|B) (A|C) is the intersection type of A|B and A|C. Note further that A|B is a subtype of A and B written A|B : A and A|B : B and so on. Usually the A|B|C is called Bottom or some such. I think it is the Whatever type of Perl6. It is the glb (greatest lower bound) of A, B and C. In a larger lattice this type gets larger as well. Any is the trivial supertype of all types. This lattice can then be used for type checks and specificity comparisons in dispatch. BTW, such a lattice can be calculated lazily from any set of packages. In pure MMD the selected target has to be the most specific in all dispatch relevant positions. Regards, --
Re: class interface of roles
HaloO, TSa wrote: Note that the superclass interface of roles should be mostly inferred from the usage of next METHOD. As such it is a useful guidance for error reports in the class composition process. Actually 'next METHOD' doesn't catch all superclass interface issues. There is the simple case of calling e.g. accessor methods on super which should result in the requirement to provide them. So I still propose a super keyword that in roles means the object as seen from the uncomposed class. Regards, --
Re: class interface of roles
HaloO, Stevan Little wrote: I do not think method combination should be the default for role composition, it would defeat the composeability of roles because you would never have conflicts. I don't get that. The type system would give compile time errors. The current spec means that in case of a conflicting method the class version simply overrides the role version. That is there is simple, short or long name based conflict checking with priority to the class. I see that quite different: roles are the primary carrier of type information! Well yes, they do seem to have taken on this role ;). If it is not roles that carry type information then the Perl6 type system is as of now completely unspecced. Even with roles I miss some clear statements about their theoretical background. However, roles as originally envisioned in the Traits paper are not related to the type system, but instead related to class/object system. In fact the Trait paper gave it's examples in Smalltalk, which is not a strongly typed language (unless you count the idea that *everything* is an object and therefore that is their type). Remember the paper did not include state for traits and thus nicely avoided several typing issues particularly in SmallTalk that is based on single inheritance and dispatch along these lines. I think we need to be careful in how we associate roles with the type system and how we assocaite them with the object system. I worry that they will end up with conflicting needs and responsibilities and roles will end up being too complex to be truely useful. My current understanding is that properly typed roles can obliviate the need of the things described in theory.pod and directly go with F-bounded polymorphism as the theoretical model of the type system. It e.g. is strong enough to model Haskell type classes. Note that there are free mixins that pose no requirements on the class in the theory as described in the article. Dispatch depends on a partial ordering of roles. Type based dispatch does (MMD), but class based method dispatch doesn't need it at all. I strongly agree. The two hierarchies should be separated. The only interweaving that occurs is the class composition process. And this process should IMHO be directed by the type system and provide for method combination when the correct typing of the role requires it. Typical examples that need method combination are equality checking, sorting support and generic container types. There seems to be another connection from the class hierarchy to the role hierarchy that is that a class has a role of the same name so that class names can be used where a type is expected or however this is supposed to work. In the end there shall be some mixing of class and type based dispatch or some kind of embedding of the class dispatch into type dispatch. The whole fact that dispatching requires roles to be partially ordered actually tells me that maybe roles should not be so hinged to the type system since roles are meant to be unordered. But how else do we define a subtype relation if not through a role type lattice? Possiblely we should be seeing roles as a way of *implementing* the types, and not as a core component of the type system itself? Hmm, again what is the type system then? All indications from the Synopsis and this list go for roles taking over the responsibility of key player in the type department. E.g. type parameters also go with roles not with classes. Regards, --
Re: class interface of roles
HaloO, TSa wrote: Note that A = (A|B) (A|C) is the intersection type of A|B and A|C. Note further that A|B is a subtype of A and B written A|B : A and A|B : B and so on. Usually the A|B|C is called Bottom or some such. I think it is the Whatever type of Perl6. It is the glb (greatest lower bound) of A, B and C. In a larger lattice this type gets larger as well. Any is the trivial supertype of all types. Damn it! I always puzzle glb and lub (least upper bound). So it should read lub there. This is because the intension set gets larger even though a subtype is formed. Note that the extension set the instances becomes smaller! In a limiting case the Whatever type has got no instances at all :) Sorry, --
Re: Re: class interface of roles
Jonathan~ On 10/7/06, Jonathan Lang [EMAIL PROTECTED] wrote: TSa wrote: Dispatch depends on a partial ordering of roles. Could someone please give me an example to illustrate what is meant by partial ordering here? Sets demonstrate partial ordering. Let denote the subset relation ship. If A B and B C, then A C for any A, B, and C. However, it is not necessarily the case that A B, or B A, or B == A for any particular A and B. Thus transitivity is preserved, but there is not a guarantee of comparability between elements. http://en.wikipedia.org/wiki/Partial_ordering Matt -- Computer Science is merely the post-Turing Decline of Formal Systems Theory. -Stan Kelly-Bootle, The Devil's DP Dictionary
Re: Re: class interface of roles
On 10/6/06, TSa [EMAIL PROTECTED] wrote: HaloO, Stevan Little wrote: As for how the example in the OP might work, I would suspect that super would not be what we are looking for here, but instead a variant of next METHOD. I'm not familiar with the next METHOD syntax. How does one get the return value from it and how are parameters passed? Would the respective line in the equal method then read: return next METHOD($p) and self.x == $p.x and self.y == $p.y; I think that a super keyword might be nice syntactic sugar for this. I think super is not something we would want in Roles, it implies ordering, which defeats the flattening aspect of Roles. IIRC the syntax to call the next method with new args and a return value is this: return call($p) and self.x == $p.x and self.y == $p.y; However, I am not sure I really like the look of that myself. However, even with that an ordering of some kind is implied The only ordering I see is that the class is up from the role's perspective. But thats the whole problem, there is no up in roles, they are flattened. When more then one role is combined and all require the presence of an equal method I think the roles can be combined in any order and the super refers to the class combined so far. IOW, at any given time in the composition process there is a current version of the class' method. The final outcome is a method WALK or however this is called in composition order. Conceptually this is method combination: seen from outside the class has just one type correct method equal. Theoretical background can be found in http://www.jot.fm/issues/issue_2004_01/column4 I do not think method combination should be the default for role composition, it would defeat the composeability of roles because you would never have conflicts. However, I can see the possibility of method combinations in roles being some kind of special case. How that might look from a syntactic perspective I have no idea. I suppose this is again where the different concepts of classes are roles can get very sticky. I have always look at roles, once composed into the class, as no longer needing to exist. In fact, if it weren't for the idea of runtime role compostion and runtime role introspection, I would say that roles themselves could be garbage collected at the end of the compile time cycle. I see that quite different: roles are the primary carrier of type information! Well yes, they do seem to have taken on this role ;). However, roles as originally envisioned in the Traits paper are not related to the type system, but instead related to class/object system. In fact the Trait paper gave it's examples in Smalltalk, which is not a strongly typed language (unless you count the idea that *everything* is an object and therefore that is their type). I think we need to be careful in how we associate roles with the type system and how we assocaite them with the object system. I worry that they will end up with conflicting needs and responsibilities and roles will end up being too complex to be truely useful. Dispatch depends on a partial ordering of roles. Type based dispatch does (MMD), but class based method dispatch doesn't need it at all. The whole fact that dispatching requires roles to be partially ordered actually tells me that maybe roles should not be so hinged to the type system since roles are meant to be unordered. Possiblely we should be seeing roles as a way of *implementing* the types, and not as a core component of the type system itself? I did some experimentation with this in Moose::Autobox, but that is for Perl 5 so I am not sure how relevant it is here. - Stevan
Re: Re: class interface of roles
On 10/6/06, TSa [EMAIL PROTECTED] wrote: HaloO, Stevan Little wrote: On 10/2/06, Jonathan Lang [EMAIL PROTECTED] wrote: This notion of exclusionary roles is an interesting one, though. I'd like to hear about what kinds of situations would find this notion useful; but for the moment, I'll take your word that such situations exist and go from there. Well to be honest, I haven't found a real-world usage for it yet (at least in my travels so far), but the Fortress example was this: trait OrganicMolecule extends Molecule excludes { InorganicMolecule } end trait InorganicMolecule extends Molecule end Wouldn't that be written in Perl6 the other way around? role OrganicMolecule {...} role InorganicMolecule {...} role Molecule does OrganicMolecule ^ InorganicMolecule {...} Which is a nice usage of the xor role combinator. Well, it does seem to accomplish a similar goal. However, in your example there is nothing about the OrganicMolecule which will prevent me from composing it with the InOrganicMolecule, which was the primary goal of the Fortress example. In addition in the Fortress example the Molecule role is not coupled to the Organic and Inorganic molecules, in your example they are. But IMHO this is just another example of TIMTOWTDI, because your example achieves similar goals and would likely be a valid design approach as well. And from that I could see that given a large enough set of roles you would surely create roles which conflicted with one another on a conceptual level rather then on a methods/attribute (i.e. - more concrete) level. I don't abide to that. If roles are conceptually modelling the same entity their vocabulary should conflict also. Well unless some differing coding conventions accidentally produce non-conflicting roles. The whole point of type systems relies on the fact that concrete conflicts indicate conceptual ones! But part of the power of role composability is that the role itself does not need to dictate what class it is composed into. So conceptual conflicts cannot be determined until compostion actually occurs, and conflicts between two conceptually conflicting roles cannot be detected until composition time either. And of course there is nothing to say that two conceptually conflicting roles have a concrete conflict either (either between methods or attributes). I think that maybe we need to seperate the concept of roles as types and roles as partial classes, they seem to me to be in conflict with one another. And even they are not in conflict with one another, I worry they will bloat the complexity of roles usage. My experiences thus far with roles in Moose have been that they can be a really powerful means of reuse. I point you towards Yuval Kogman's latest work on Class::Workflow, which is basically a loose set of roles which can be composed into a highly customizable workflow system. This is where I see the real power of roles coming into play. - Stevan
Re: Re: class interface of roles
TSa wrote: Dispatch depends on a partial ordering of roles. Could someone please give me an example to illustrate what is meant by partial ordering here? -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO, Stevan Little wrote: As for how the example in the OP might work, I would suspect that super would not be what we are looking for here, but instead a variant of next METHOD. I'm not familiar with the next METHOD syntax. How does one get the return value from it and how are parameters passed? Would the respective line in the equal method then read: return next METHOD($p) and self.x == $p.x and self.y == $p.y; I think that a super keyword might be nice syntactic sugar for this. However, even with that an ordering of some kind is implied The only ordering I see is that the class is up from the role's perspective. When more then one role is combined and all require the presence of an equal method I think the roles can be combined in any order and the super refers to the class combined so far. IOW, at any given time in the composition process there is a current version of the class' method. The final outcome is a method WALK or however this is called in composition order. Conceptually this is method combination: seen from outside the class has just one type correct method equal. Theoretical background can be found in http://www.jot.fm/issues/issue_2004_01/column4 I suppose this is again where the different concepts of classes are roles can get very sticky. I have always look at roles, once composed into the class, as no longer needing to exist. In fact, if it weren't for the idea of runtime role compostion and runtime role introspection, I would say that roles themselves could be garbage collected at the end of the compile time cycle. I see that quite different: roles are the primary carrier of type information! Dispatch depends on a partial ordering of roles. I think all roles will form a type lattice that is available at runtime for type checks. With parametric roles there will be dynamic instanciations as needed. Regards, --
Re: class interface of roles
HaloO, Stevan Little wrote: On 10/2/06, Jonathan Lang [EMAIL PROTECTED] wrote: This notion of exclusionary roles is an interesting one, though. I'd like to hear about what kinds of situations would find this notion useful; but for the moment, I'll take your word that such situations exist and go from there. Well to be honest, I haven't found a real-world usage for it yet (at least in my travels so far), but the Fortress example was this: trait OrganicMolecule extends Molecule excludes { InorganicMolecule } end trait InorganicMolecule extends Molecule end Wouldn't that be written in Perl6 the other way around? role OrganicMolecule {...} role InorganicMolecule {...} role Molecule does OrganicMolecule ^ InorganicMolecule {...} Which is a nice usage of the xor role combinator. And from that I could see that given a large enough set of roles you would surely create roles which conflicted with one another on a conceptual level rather then on a methods/attribute (i.e. - more concrete) level. I don't abide to that. If roles are conceptually modelling the same entity their vocabulary should conflict also. Well unless some differing coding conventions accidentally produce non-conflicting roles. The whole point of type systems relies on the fact that concrete conflicts indicate conceptual ones! Regards, --
Re: class interface of roles
TSa wrote: I'm not familiar with the next METHOD syntax. It's simple: if a multi method says next METHOD; then execution of the current method gets aborted, and the next MMD candidate is tried; it uses the same parameters that the current method used, and it returns its value to the current method's caller. In effect, next METHOD is an aspect of MMD that allows an individual method to say I'm not the right guy for this job, and to punt to whoever's next in line. If not for the possibility of side effects that occur before the punt, one could pretend that the current method was never tried in the first place. I see that quite different: roles are the primary carrier of type information! Dispatch depends on a partial ordering of roles. I think all roles will form a type lattice that is available at runtime for type checks. True: the relationships between various roles and classes (who does what?) is needed for runtime type checking. However, the _contents_ of the roles are only important for composing classes and for the occasional runtime introspection of a role. If roles are never composed or inspected at runtime, the only details about them that need to be kept are who does what? - and if all type-checking takes place at compile-time, not even this is needed. But now we're getting dangerously close to perl6internals territory... -- Jonathan Dataweaver Lang
Re: class interface of roles
HaloO, Brad Bowman wrote: Sam Vilain wrote: This will be the same as requiring that a class implements a method, except the method's name is infix:==(::T $self: T $other) or some such. Sure. The point is, how does a role designer mix in the x and y coordinate attributes *and* augment the notion of equality to encompass these new attributes *without* shifting this burden onto the class implementor! Does the class GenSquare does GenEqual does GenPointMixin line imply an ordering of class composition? This would seem to be required for the super.equal hand-wave to work but part of the Traits Paper goodness came from avoiding an ordering. Composition is just order insensitive flattening. Conflicts like the equal method in the OP have to be explicitly resolved in the target class, either using aliases or fully qualified names. So there's no super needed. Hmm, my aim was more at the class composition process as such. I envision a type bound calculation for all composed roles. This bound then is available as super to all roles. Inter-role conflicts are orthogonal to this. If the bound is fulfilled the order of role composition doesn't matter. In the case of the equality checks the different participants of the composition call each other and use logical and to yield the final equality check. To the outside world the equality method appears to be a single call on the type of the objects created from the class. Note that it is type correct in the sense that all participants are considered. My hope is that this is achieved automatically as outcome of the composition process and not by intervention of the class implementor. (I should confess that I haven't yet read the OP linked article), To understand it you might actually need to read previous articles in the series, too. Regards, --
Re: class interface of roles
Sam Vilain wrote: TSa wrote: is this subject not of interest? I just wanted to start a discussion about the class composition process and how a role designer can require the class to provide an equal method and then augment it to achieve the correct behavior. Contrast that with the need to do the same in every class that gets the equal method composed into if the role doesn't have a superclass interface as described in the article. This will be the same as requiring that a class implements a method, except the method's name is infix:==(::T $self: T $other) or some such. How does a Role require that the target class implement a method (or do another Role)? Does the class GenSquare does GenEqual does GenPointMixin line imply an ordering of class composition? This would seem to be required for the super.equal hand-wave to work but part of the Traits Paper goodness came from avoiding an ordering. Composition is just order insensitive flattening. Conflicts like the equal method in the OP have to be explicitly resolved in the target class, either using aliases or fully qualified names. So there's no super needed. I would like a way to make one Role to require that the target class does another abstract Role, is there already such a technique? (I should confess that I haven't yet read the OP linked article), Brad
Re: Re: class interface of roles
On 10/2/06, Brad Bowman [EMAIL PROTECTED] wrote: Sam Vilain wrote: TSa wrote: is this subject not of interest? I just wanted to start a discussion about the class composition process and how a role designer can require the class to provide an equal method and then augment it to achieve the correct behavior. Contrast that with the need to do the same in every class that gets the equal method composed into if the role doesn't have a superclass interface as described in the article. This will be the same as requiring that a class implements a method, except the method's name is infix:==(::T $self: T $other) or some such. How does a Role require that the target class implement a method (or do another Role)? IIRC, it simply needs to provide a method stub, like so: method bar { ... } This will tell the class composer that this method must be created before everything is finished. Does the class GenSquare does GenEqual does GenPointMixin line imply an ordering of class composition? This would seem to be required for the super.equal hand-wave to work but part of the Traits Paper goodness came from avoiding an ordering. Composition is just order insensitive flattening. Conflicts like the equal method in the OP have to be explicitly resolved in the target class, either using aliases or fully qualified names. So there's no super needed. The super in a Role should be late bound, so will have no relevance when inside the role, but only make sense when composed into a class. This is probably one of the more confusing points of roles I think. As for how the example in the OP might work, I would suspect that super would not be what we are looking for here, but instead a variant of next METHOD. However, even with that an ordering of some kind is implied. I suppose this is again where the different concepts of classes are roles can get very sticky. I have always look at roles, once composed into the class, as no longer needing to exist. In fact, if it weren't for the idea of runtime role compostion and runtime role introspection, I would say that roles themselves could be garbage collected at the end of the compile time cycle. I would like a way to make one Role to require that the target class does another abstract Role, is there already such a technique? I am not familiar with one, but I have had this need as well lately in using Moose roles. We have a concept in Moose (stolen from the Fortress language) where a particular role can exclude the use of another role, but not the ability to require it, although I see no reason why it couldn't be done. - Stevan
Re: class interface of roles
Brad Bowman wrote: Hi, Did you mean to go off list? No, I didn't. Jonathan Lang wrote: Brad Bowman wrote: Does the class GenSquare does GenEqual does GenPointMixin line imply an ordering of class composition? No. This was a conscious design decision: the order in which you compose roles has no effect on the result. Great. That's what I want. I would like a way to make one Role to require that the target class does another abstract Role, is there already such a technique? What's wrong with just having the role compose the other abstract role itself? That is, instead of role Bar requires Baz; class Foo does Bar does Baz, why not say role Bar does Baz; class Foo does Bar? That would work, as long as you get a compile time error when Foo doesn't implement the Baz abstract interface. ...which is exactly what happens. Mind you, the compile-time error won't report that Baz::method is unimplemented, or even that Bar::method is unimplemented; it will report that Foo::method is unimplemented. It's up to the programmer to figure out where Foo::method came from, on the off chance that it matters. It's perhaps also less clear that the indirect Baz mixin must be implemented. If Bar does Baz, you can read that as Bar requires Baz to be implemented, too. There's a tendency, when dealing with traditional inheritance systems, to think of the primary function of a superclass as being a supplier of implementations for any classes that inherit from it. I submit that this is the wrong way to think of roles: rather, a role is first and foremost a source of interface requirements for whatever does that role. If a role includes a method declaration, that should be read as anything that does this role must provide a method that matches this one's name and signature. If a role does another role, that should be read as anything that does this role should conform to the requirements of this other role as well. Any implementations that a role provides should be viewed as _sample_ implementations, to be used if and only if you can find no reason not to use them. BTW, this includes attributes: if a role declares a public attribute, this should be read as that role requiring an accessor method for that attribute; if whatever does the role redefines the methods (including the accessor method) such that none of them refer to the attribute, the attribute should not be mixed in. If a role defines a private attribute and then fails to define any methods that access that attribute, the only way that that attribute should end up getting mixed into something else is if whatever does the role that the attribute is in provides the methods in question. In summary, attributes and method bodies in roles should be taken as _suggestions_ - only the public method names and signatures should be viewed as requirements. I guess any role could just declare some yada methods and leave it at that. That too. Bear in mind that when you compose a role into another role, you are under no obligation to replace yada methods with defined ones. In fact, it's even conceivable for you to replace a defined method with a yada method if the default implementation from the other role isn't suited to the current one. The only time that you're required to replace yada methods with defined methods is when you compose into a class. -- Jonathan Dataweaver Lang
Re: Re: class interface of roles
Stevan Little wrote: Brad Bowman wrote: How does a Role require that the target class implement a method (or do another Role)? IIRC, it simply needs to provide a method stub, like so: method bar { ... } This will tell the class composer that this method must be created before everything is finished. Correct. I suppose this is again where the different concepts of classes are roles can get very sticky. I have always look at roles, once composed into the class, as no longer needing to exist. In fact, if it weren't for the idea of runtime role compostion and runtime role introspection, I would say that roles themselves could be garbage collected at the end of the compile time cycle. Again, you've hit the nail on the head. To elaborate on this a little bit, the only reason that perl needs to keep track of a role hierarchy at all is for parameter matching purposes (if Foo does Bar and Bar does Baz, Foo can be used if a signature asks for Baz). I would like a way to make one Role to require that the target class does another abstract Role, is there already such a technique? I am not familiar with one, but I have had this need as well lately in using Moose roles. We have a concept in Moose (stolen from the Fortress language) where a particular role can exclude the use of another role, but not the ability to require it, although I see no reason why it couldn't be done. As I mentioned before, having role Bar require that Baz also be composed is a simple matter of saying role Bar does Baz. This notion of exclusionary roles is an interesting one, though. I'd like to hear about what kinds of situations would find this notion useful; but for the moment, I'll take your word that such situations exist and go from there. I wonder if it would be worthwhile to extend the syntax of roles so that you could prepend a no on any declarative line, resulting in a compilation error any time something composing that role attempts to include the feature in question. So, for instance, you might have role Bar { no method baz (Num, Str); } class Foo does Bar { method baz (Num $n, Str $s) { ... } # compilation error: Bar forbade this method! } or role Bar no does Baz { # granted, the english grammar is all wrong... } class Foo does Bar does Baz { # compilation error: Bar forbade the inclusion of Baz! } This is not the same as removing something that a composed role brought in, which is a separate potentially useful notion. The former is Foo doesn't play well with Bar; so don't try to use them together; the latter is Foo can do _almost_ everything Bar can, but not quite. Mind you, if I ever see something to the effect of Foo does Bar except baz() as valid syntax, I'll expect a query to the effect of Foo does Bar? to answer to the negative. This would include can Foo be used when Bar is asked for? -- Jonathan Dataweaver Lang
Re: class interface of roles
On Monday 02 October 2006 08:58, Jonathan Lang wrote: I wonder if it would be worthwhile to extend the syntax of roles so that you could prepend a no on any declarative line, resulting in a compilation error any time something composing that role attempts to include the feature in question. So, for instance, you might have role Bar { no method baz (Num, Str); } class Foo does Bar { method baz (Num $n, Str $s) { ... } # compilation error: Bar forbade this method! } This feels like the false-cognate problem waiting to creep back in. -- c
Re: Re: Re: class interface of roles
On 10/2/06, Jonathan Lang [EMAIL PROTECTED] wrote: This notion of exclusionary roles is an interesting one, though. I'd like to hear about what kinds of situations would find this notion useful; but for the moment, I'll take your word that such situations exist and go from there. Well to be honest, I haven't found a real-world usage for it yet (at least in my travels so far), but the Fortress example was this: trait OrganicMolecule extends Molecule excludes { InorganicMolecule } end trait InorganicMolecule extends Molecule end And from that I could see that given a large enough set of roles you would surely create roles which conflicted with one another on a conceptual level rather then on a methods/attribute (i.e. - more concrete) level. - Stevan
Re: class interface of roles
TSa wrote: HaloO, is this subject not of interest? I just wanted to start a discussion about the class composition process and how a role designer can require the class to provide an equal method and then augment it to achieve the correct behavior. Contrast that with the need to do the same in every class that gets the equal method composed into if the role doesn't have a superclass interface as described in the article. This will be the same as requiring that a class implements a method, except the method's name is infix:==(::T $self: T $other) or some such. Sam.
Re: class interface of roles
HaloO, is this subject not of interest? I just wanted to start a discussion about the class composition process and how a role designer can require the class to provide an equal method and then augment it to achieve the correct behavior. Contrast that with the need to do the same in every class that gets the equal method composed into if the role doesn't have a superclass interface as described in the article. Regards, --