RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)
Em Sex, 2009-07-10 às 15:39 -0700, Jon Lang escreveu: The key to understanding roles is to note that roles don't implement methods; classes implement methods. Er, while I see your point, Roles are not just interfaces... they are OO components that can be plugged into other classes. They often are used for type identity exactly because of that attribute, since you won't be enforcing any hierarchy. Roles define which methods must be implemented, and suggest ways that they might be implemented; classes decide which implementation to use. Anything that breaks this paradigm is a Bad Thing. That's not the common conception in Roles usage, specially in Moose. As I said, Roles are not just interfaces, they are OO reuseable components. The spec itself says: Classes are primarily for instance management, not code reuse. Consider using Croles when you simply want to factor out common code. The key issue here is Perl 6 wasn't yet used to the extent that Moose::Roles are, and Moose people have identified that the use of Roles as reusable components raised the issue when the class inadvertedly overrides one of the methods that are implemented by one of the composed roles. I did think that this should be the expected behavior, but when the people that is heavily using it says it took me a lot of time to debug, it indicates that there's something wrong with the behavior. So now I changed my mind, inheritance is about overriding behavior, so when you implement a method in the subclass it is a natural thinking that this should override the superclass, but when you think about it really carefully this logic doesn't really map well to Roles (considering roles as OO reuseable components). That being said, I'd think the following as an interesting solution: role R1 { method foo() {...} # degenerates to interface } role R2 does R1 { method bar() { # some implementation } method baz() { # some implementation } } class Bla does R2 { method foo { # implementing here is natural, since the role only # declared a stub, it's even a warning not to implement it } supersede method bar { # explicitly tells that I want to ignore the implementation # in the role. nextsame wouldn't find the role implementation. } augment method baz { # explicitly tells that I want to provide an additional # implementation besides the one in the role. nextsame would find # the role implementation. } } In the above example, declaring a method without either supersede or augment would result in a compile-time warning, while using augment semantics by default. dainel
.match and .subst set outer $/?
payload++ brought this up on #perl6: in current Rakudo, $string ~~ /re/ sets $/ in the scope in which the expression appears, ie 'a' ~~ /./; say $/; # ouput: a But $str.match(..) and $str.subst don't. The spec is rather silent, it says There are also method forms of m// and s///: [...] There is no syntactic sugar here. I setting of OUTER::$/ considered syntactic sugar? I don't care either way, I'd just like some clarification so that I can write tests and submit tickets (if appropriate). Cheers, Moritz
Re: .match and .subst set outer $/?
Em Dom, 2009-07-12 às 22:51 +0200, Moritz Lenz escreveu: I setting of OUTER::$/ considered syntactic sugar? I don't care either way, I'd just like some clarification so that I can write tests and submit tickets (if appropriate). As far as I remember, it's not really OUTER::$/, but each routine implicitly declare my $/ is contextrw; my $! is contextrw; so what happens inside m// or s/// is that inside that it should look for $*/, as well as the process of failing should look for $*!. This also has the advantage of: { 'abc' ~~ /abc/; say $/; # prints abc { my $/ is contextrw; 'bcd' ~~ /bcd'; say $/; # prints bcd; } say $/; # still prints abc; } I'm pretty sure that was just said by TimToady on IRC a lot of time ago and no spec actually defines it. That being said, I don't think there's a reason for .match and .subst not to look and set $*/. daniel
Re: Reusing code: Everything but the kitchen sink
On 2009-Jul-10, at 4:37 pm, Jon Lang wrote: This is one of the distinctions between role composition and class inheritance. With class inheritance, the full tree of inherited classes is publicly accessible; with role composition, the methods of the combined role are the only ones that are made available to the class. OK, that's actually about what I was thinking, despite the peculiar way I expressed it. I meant the full names to refer to methods directly in the composed role, not somewhere else. Of course, there's already a way to refer to methods with the same name -- using the long name that includes the signature. So my example should have used bark(Canine: ...) and bark(Tree: ...); and whichever one actually gets called depends on whether the invocant does Canine or does Tree. so Dogwood::bark ought to consider its context (am I being called to behave like a Canine, a Tree, or something else?) and decide what to do based on that. If Dogwood::bark isn't defined, you should get an implementation conflict error, because the class failed in its duty to provide an implementation. Yes, and Dogwood::bark could handle it by something like: if $self.does(Canine) {...} elsif $self.does(Tree) {...} -- but Perl already knows how to handle multiple dispatch based on type, so I shouldn't have to write it out manually. In fact, this works with Rakudo: you can have both barks if you declare them as multis, and then it will accept them without having to declare a Dogwood::bark. (But of course if you try calling it, you get an Ambiguous dispatch to multi 'bark' error, because a $dogwood object equally satisfies both signatures.) (I tried to see what would happen if you cast the $dogwood object to Canine or to Tree, but either Rakudo doesn't do it yet, or I got it wrong.) Needing to say multi makes sense if you wanted multiple methods of the same name *within* a role (or class or any other namespace), but I don't think it should be necessary across different Roles. Since they already exist in different namespaces, we know they're supposed to mean different things, and it's a simple fact of life that sometimes the same term will get used in different places for completely different meanings. If you have to do the dispatching manually, I guess that's only a slight annoyance as long as it's possible. (Maybe it's better to force the programmer to do it, not because Perl couldn't, but to prevent potential surprises? Hm.) role R { method foo() { say foo } role R1 does R { method bar() { say bar } role R2 does R { method baz() { say baz } class C does R1 does R1 { } The question is whether or not Rakudo is smart enough to realize that R1::foo is the same as R2::foo, or if it complains that R1 and R2 are both trying to supply implementations for foo. The former is the desired behavior. Conversely, in this case the same name means the same thing, so it does seem perl ought to be able to tell that both foo's are really a single foo() here; since they both come from the same role (R), they have to mean the same thing, and C has to know that it does R. In any case, then the question is how to know what role something does, which is really a question about casting and passing args rather than anything to do with Roles per se. I can't tell just from $dogwood.bark which kind of barking is wanted; but I could have Dogwood::bark_like_a_dog() instead, perhaps. However, in sub nighttime (Canine $rover) { $rover.bark if any(burglars()); } I can only call .bark because all I know for sure is that I have something which does Canine; if I pass it a $dogwood object, I see three possibilities: 1) $rover in the sub is just the Dogwood object that was passed in, and calling $rover.bark cannot know what to do. I also can't call $rover.bark_like_a_dog or anything else, because that method exists only for Dogwood objects, and the sub doesn't always receive Dogwoods. So I'm stuck, and I don't see any way around that the way things are. 2) $rover does Canine and only Canine -- the Tree-half of $dogwood that was passed in is invisible inside the sub, and thus $rover.bark calls bark(Canine:) which is what we want. (Of course, it calls Dogwood's bark(Canine:) when passed a Dogwood object -- it's not magically jumping back to the original Canine role.) If nighttime() in turn calls something-else($rover), the something-else sub also gets only a Canine object. 3) $rover acts like a Canine, but the rest of the original $dogwood arg (the Tree parts) are still there; they just aren't used unless somehow explicitly brought out; for example, by casting $rover to a Tree, or by passing it to some other function that is looking for a Tree object. This is how I'd like it to work, because that's the most flexible. Maybe there should be hard casting and soft casting: by hard
Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)
On 2009-Jul-12, at 12:43 pm, Daniel Ruoso wrote: role R1 { method foo() {...} # degenerates to interface } Just wondering: since merely declaring an interface will be common enough, should we be able to say simply method foo; inside a role, and drop the {...}? class Bla does R2 { method foo { # implementing here is natural, since the role only # declared a stub, it's even a warning not to implement it } supersede method bar { # explicitly tells that I want to ignore the implementation # in the role. nextsame wouldn't find the role implementation. } augment method baz { # explicitly tells that I want to provide an additional # implementation besides the one in the role. nextsame would find # the role implementation. } } Works for me. I thought having suggest to make it work the other way around sounded useful too, but perhaps you think in practice it wouldn't be worth it? -David
Re: Reusing code: Everything but the kitchen sink
On Jul 12, 2009, at 20:15 , David Green wrote: sub nighttime (Canine $rover) { $rover.bark if any(burglars()); } (...) 3) $rover acts like a Canine, but the rest of the original $dogwood arg (the Tree parts) are still there; they just aren't used unless somehow explicitly brought out; for example, by casting $rover to a Tree, or by passing it to some other function that is looking for a Tree object. This is how I'd like it to work, because that's the most flexible. If you haven't declared it as such, this strikes me as a bad thing. Perhaps some kind of declarative syntax that lets you declare that you can take a Dogwood, such that you get an added argument which is undef (for a non-Dogwood) or a Dogwood (or Tree?)? -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allb...@kf8nh.com system administrator [openafs,heimdal,too many hats] allb...@ece.cmu.edu electrical and computer engineering, carnegie mellon universityKF8NH PGP.sig Description: This is a digitally signed message part
Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)
Daniel Ruoso wrote: Jon Lang wrote: The key to understanding roles is to note that roles don't implement methods; classes implement methods. Er, while I see your point, Roles are not just interfaces... they are OO components that can be plugged into other classes. They often are used for type identity exactly because of that attribute, since you won't be enforcing any hierarchy. Right. But as they were originally conceived, they were interfaces that could also handle code reuse, rather than units of code reuse that could also be used as interfaces. From this perspective, it makes perfect sense that a role's methods can be overridden as easily as they are. But you make a good point: there are some (a few? most?) programmers who are going to want to use roles primarily for code reuse, and who will want it to be a little more difficult to override the code provided by a role (e.g., requiring the use of supersede and perhaps augment in order to replace the definition with a new one). First and foremost, this distinction between suggested ans mandatory implementation is what I was trying to make a little more explicit in my proposal: a suggested method can be overridden by the class with no extra effort; a mandatory method requires that the class be explicit about the override. The next question is which of these approaches Perl 6 should use with roles. Currently, it's using suggested implementations; what I'm hearing you say is that you'd rather have mandatory implementations. IMHO, there's a time ans place for both; so I was trying to come up with a compromise of sorts: a way of letting the programmer select the approach that most suits his needs. Roles define which methods must be implemented, and suggest ways that they might be implemented; classes decide which implementation to use. Anything that breaks this paradigm is a Bad Thing. That's not the common conception in Roles usage, specially in Moose. As I said, Roles are not just interfaces, they are OO reuseable components. FWIW, I never said that they're just interfaces. Also, I question whether that is or is not the common conception of role usage. I readily admit that it isn't so in the programming circles that you travel in; but are you typical of the perl community in this regard? This is not a rhetorical question; the way that we end up addressing this issue hinges on this question: should roles provide suggested implementations by default, or should they provide mandatory implementations by default? Even if Perl is rich enough to provide for both, the decision of which way to go when no explicit decision has been made is an important one. The spec itself says: Classes are primarily for instance management, not code reuse. Consider using Croles when you simply want to factor out common code. Right: roles are preferable to classes when it comes to code reuse. That doesn't necessarily mean that roles are _primarily_ intended for code reuse. They _might_ be; but if so, it's because they've grown beyond their original concept. The key issue here is Perl 6 wasn't yet used to the extent that Moose::Roles are, and Moose people have identified that the use of Roles as reusable components raised the issue when the class inadvertedly overrides one of the methods that are implemented by one of the composed roles. You know what? Until Moose was mentioned in this conversation, I had never heard of it. I did think that this should be the expected behavior, but when the people that is heavily using it says it took me a lot of time to debug, it indicates that there's something wrong with the behavior. So now I changed my mind, inheritance is about overriding behavior, so when you implement a method in the subclass it is a natural thinking that this should override the superclass, but when you think about it really carefully this logic doesn't really map well to Roles (considering roles as OO reuseable components). That may indeed be the case. It's entirely possible that we may want to change things so that roles define mandated methods, and possibly introduce interfaces as a variation of roles that define suggested methods. But we may instead want to keep roles as they are, and define some other variation that works just like a role except that it mandates its methods. And its also possible that I'm fundamentally wrong about this, and that we _don't_ need both approaches available for roles. That being said, I'd think the following as an interesting solution: role R1 { method foo() {...} # degenerates to interface } role R2 does R1 { method bar() { # some implementation } method baz() { # some implementation } } class Bla does R2 { method foo { # implementing here is natural, since the role only # declared a stub, it's even a warning not to implement it } supersede method bar { # explicitly tells that I want to ignore the