Re: YAPC::EU and Perl 6 Roles
- Original Message From: Timothy S. Nelson wayl...@wayland.id.au class PracticalJoke { has Bomb $bomb handles ; has Spouse $spouse handles ; } Note that I have no idea where (if anywhere) the type goes in this. Hopefully someone will correct me here. Note that this does not use the roles as roles; it uses them punned as classes. But it does what you asked :). Though I have issues with Jonathan's approach (I don't like classes silently discarding role methods as this has caused us many bugs at the BBC), it's much cleaner that what I see here. You see, with Jonathan's, you only have to provide methods for what you're disambiguating, It seems like your code would require that I specifically list every method which is handled, which would clearly get unwieldy with large roles or many roles. Did I miss something? Cheers, Ovid -- Buy the book - http://www.oreilly.com/catalog/perlhks/ Tech blog- http://use.perl.org/~Ovid/journal/ Twitter - http://twitter.com/OvidPerl Official Perl 6 Wiki - http://www.perlfoundation.org/perl6
Re: YAPC::EU and Perl 6 Roles
On Wed, 8 Jul 2009, Ovid wrote: Note that I have no idea where (if anywhere) the type goes in this. Hopefully someone will correct me here. Note that this does not use the roles as roles; it uses them punned as classes. But it does what you asked :). Though I have issues with Jonathan's approach (I don't like classes silently discarding role methods as this has caused us many bugs at the BBC), it's much cleaner that what I see here. You see, with Jonathan's, you only have to provide methods for what you're disambiguating, It seems like your code would require that I specifically list every method which is handled, which would clearly get unwieldy with large roles or many roles. Did I miss something? I agree that's a problem, and hopefully one that should be solved. The solution I offered solves only your immediate problem. :) - | Name: Tim Nelson | Because the Creator is,| | E-mail: wayl...@wayland.id.au| I am | - BEGIN GEEK CODE BLOCK Version 3.12 GCS d+++ s+: a- C++$ U+++$ P+++$ L+++ E- W+ N+ w--- V- PE(+) Y+++ PGP-+++ R(+) !tv b++ DI D G+ e++ h! y- -END GEEK CODE BLOCK-
Re: YAPC::EU and Perl 6 Roles
Ovid wrote: - Original Message From: Timothy S. Nelson wayl...@wayland.id.au class PracticalJoke { has Bomb $bomb handles ; has Spouse $spouse handles ; } Note that I have no idea where (if anywhere) the type goes in this. Hopefully someone will correct me here. Note that this does not use the roles as roles; it uses them punned as classes. But it does what you asked :). Though I have issues with Jonathan's approach (I don't like classes silently discarding role methods as this has caused us many bugs at the BBC), it's much cleaner that what I see here. s/Jonathan's approach/Perl 6's approach/ # at least, so far as I understand Perl 6 Jonathan
Re: YAPC::EU and Perl 6 Roles
Jonathan Worthingtonjonat...@jnthn.net wrote: Ovid wrote: Though I have issues with Jonathan's approach (I don't like classes silently discarding role methods as this has caused us many bugs at the BBC), it's much cleaner that what I see here. s/Jonathan's approach/Perl 6's approach/ # at least, so far as I understand Perl 6 No; I think he meant Jonathan's approach, as in the suggestion that I made earlier about disambiguating by exclusion. It occurs to me that but might be a way to approach this, since the linguistic purpose of that operator is to generate a class or role that is a slight departure from an existing role: A but without(foo) might generate an anonymous role that's exactly like A except that it doesn't have the foo method; you could then say something like: role A { method foo() { }; method bar() { }; method baz() { } } role B { method foo() { }; method bar() { }; method baz () { } } class C does A but without(foo, bar) does B but without(baz) { } I agree that there are dangers involved in excluding methods from a role; thus my use of fairly lengthy phrases in order to accomplish it - a sort of handle with care warning. Alternatively, I could see a version of this exclusionary policy being done through method delegation, by means of the whatever splat - something like: class C { has A $a handles * - (foo, bar); has B $b handles * - baz; } -- Jonathan Dataweaver Lang
Re: YAPC::EU and Perl 6 Roles
Jon Lang wrote: Jonathan Worthingtonjonat...@jnthn.net wrote: Ovid wrote: Though I have issues with Jonathan's approach (I don't like classes silently discarding role methods as this has caused us many bugs at the BBC), it's much cleaner that what I see here. s/Jonathan's approach/Perl 6's approach/ # at least, so far as I understand Perl 6 No; I think he meant Jonathan's approach, as in the suggestion that I made earlier about disambiguating by exclusion. Oh, sorry. ETOOMANYJONATHANS. ;-) It occurs to me that but might be a way to approach this, since the linguistic purpose of that operator is to generate a class or role that is a slight departure from an existing role: A but without(foo) might generate an anonymous role that's exactly like A except that it doesn't have the foo method; you could then say something like: role A { method foo() { }; method bar() { }; method baz() { } } role B { method foo() { }; method bar() { }; method baz () { } } class C does A but without(foo, bar) does B but without(baz) { } But Cbut composes in a role to a copy of the object, so it's not really following the semantics we want here, and also means we need to make without mean something special other than just being a role name. And we'd have to tinker quite a bit with the way trait_mod:does parses to make it handle this. I guess we could do that somehow, but it feels odd. Also, trying to look up foo - which would be in a completely separate role - is going to be odd too. It feels like we're making far too many things mean something a bit different. More fitting to me would be an adverb to the does trait modifier... class C does R1 :withoutfoo bar does R2 :withoutbaz { ... } The thing is that in this case, does the class actually do R1 and R2? If you are going to derive an anonymous role with the methods missing, then answer is no. That is, C ~~ R1 would be false. So I think it'd need to act as a modifier to the action of composition, rather than a modification to the thing that we're composing. I agree that there are dangers involved in excluding methods from a role; thus my use of fairly lengthy phrases in order to accomplish it - a sort of handle with care warning. I wonder if we'd want to mandate that a method of the name must come from _somewhere_ otherwise it's an error. At least then you get a promise that a method of that name exists...which is about all that it does this role tells you as an interface contract anyway. Alternatively, I could see a version of this exclusionary policy being done through method delegation, by means of the whatever splat - something like: class C { has A $a handles * - (foo, bar); has B $b handles * - baz; } The RHS of the handles is something we smart-match the method name against (unless it's one of the special syntactic cases). And thus if you care about performance you probably don't want to be falling back to handles to do your role composition, since it's kind of the last resort after we've walked the MRO and found nothing. Anyway, you'd put something on the RHS maybe like: has A $a handles none(foo bar) But I'm not sure that will fall through to B for anything that A doesn't define other than those two. You'd perhaps just get a dispatch error if you said A handles everything but those and it didn't. So it'd probably look more like... has A $.a handles all(any(A.^methods.name), none(foo bar)); Which you almost certainly don't want to be writing. ;-) Jonathan
Re: YAPC::EU and Perl 6 Roles
Hi, Going back to the original question... Ovid wrote: It needs the timed fuse() from a Bomb role and a non-lethal explode() from a Spouse role, though each role provides both methods. I'm curious... 1) How often do you in real life find yourself needing to do things like this in real life? This is a sort of strained, if amusing, example. :-) 2) A lot of me wonders if a need to exclude a method from a role is a hint that the role does too many things and should be decomposed into smaller pieces, such that it can be applied in a more granular way? I'm curious to hear the experiences of Ovid and others working with roles a lot too. Is this a serious lacking in Perl 6's roles as currently specified, or something that, in being absent, makes people consider their design more? Knowing that will influence the solution we choose, which has options ranging from, yes, make a neat syntax for it through leave it out of the core, and if people want it enough it can be a CPAN module. Thanks, Jonathan
Reusing code: Everything but the kitchen sink
Jonathan Worthington wrote in YAPC::EU and Perl 6 Roles: More fitting to me would be an adverb to the does trait modifier... class C does R1 :withoutfoo bar does R2 :withoutbaz { ... } The thing is that in this case, does the class actually do R1 and R2? If you are going to derive an anonymous role with the methods missing, then answer is no. That is, C ~~ R1 would be false. So I think it'd need to act as a modifier to the action of composition, rather than a modification to the thing that we're composing. Moving the adverb from the role to the trait modifier would still violate the notion that C ~~ R1; so it doesn't actually gain us anything. Instead, consider the following possibility: R1 :withoutfoo bar is a separate but related role from R1. The implicit relationship between them is that R1 ~~ R1 :withoutfoo bar. So C ~~ R1 would be false; but C ~~ R1 :withoutfoo bar would be true. I wonder if we'd want to mandate that a method of the name must come from _somewhere_ otherwise it's an error. At least then you get a promise that a method of that name exists...which is about all that it does this role tells you as an interface contract anyway. Right. Another way to handle this without establishing reverse-does relationships that I describe above would be to say that the adverb doesn't actually remove the method in question; it just suppresses its implementation. That is, given: role R1 { method foo() { say foo } } class C does :blockingfoo R1 { ... } this would compose R1 into C, but would discard the implementation of R1::foo while doing so. In the spirit of TIMTOWTDI, both approaches could be available: R1 :withoutfoo acts as an anonymous role that R1 implicitly does, and which is set up almost exactly like R1 except that it doesn't have method foo. This could have other uses besides role composition, such as expanding a web of roles from the specific to the general. C does :blockingfoo R1 composes R1 into C, but discards foo's implementation while doing so. Alternatively, I could see a version of this exclusionary policy being done through method delegation, by means of the whatever splat - something like: class C { has A $a handles * - (foo, bar); has B $b handles * - baz; } The RHS of the handles is something we smart-match the method name against (unless it's one of the special syntactic cases). And thus if you care about performance you probably don't want to be falling back to handles to do your role composition, since it's kind of the last resort after we've walked the MRO and found nothing. You're right, but for a different reason. Perl 6 has three techniques for code reuse, which I refer to as the be, do, have triad: Class inheritance (to be): 'is C;' Role composition (to do): 'does R;' Attribute delegation (to have): 'has A $a handles foo;' Just on a conceptual level, you don't want to fall back on attribute delegation in order to emulate role composition. Still, there are uses for delegating to all or most of an attribute's methods. Anyway, you'd put something on the RHS maybe like: has A $a handles none(foo bar) But I'm not sure that will fall through to B for anything that A doesn't define other than those two. You'd perhaps just get a dispatch error if you said A handles everything but those and it didn't. So it'd probably look more like... has A $.a handles all(any(A.^methods.name), none(foo bar)); Which you almost certainly don't want to be writing. ;-) Right; which is why I was looking for a more abbreviated form. If we go with the idea that we're using a Set on the RHS, then '*' could be shorthand for 'Set($a.^methods)', and 'Set::infix:-' could be the Set Difference operator, with the item, list, or set on the RHS being excluded from the LHS. So: $a handles * - foo bar would be short for something like: $a handles Set($a.^methods) - Set(foo, bar) This use of the Whatever splat is similar to its use in a list index, where '*' behaves as if it were the list's element count. -- Looking this over, I note that the only code reuse mechanism for which we haven't looked at the everything except... concept is class inheritance. OTOH, I think that the blocking tool that works for role composition could be adapted for use with class inheritence as well: Class C is :blockingfoo bar C1 is :blockingbaz C2 { ... } That is, if you were to search the class heierarchy for foo, it would skip the C1 branch in its search. This would have to be treated with care, because it would mean that C doesn't inherit everything from C1. This _would_ break the anything you can do I can do, too nature of class inheritence; but I'm not sure how big of a crime that is. Perl generally discourages the use of class hierarchies, and it certainly isn't the preferred means of type-checking. And maybe you could get around this by having the isa predicate return a more involved response than
Re: YAPC::EU and Perl 6 Roles
On Tue, Jul 7, 2009 at 7:13 AM, Jonathan Worthingtonjonat...@jnthn.net wrote: (Note to the bored: feel free to beat me to adding something like these last two to the spectests...I'm away for the afternoon/evening.) In r27483, I added these tests to S12-methods/multi.t: http://dev.pugscode.org/changeset/27483 Kyle.
Re: YAPC::EU and Perl 6 Roles
- Original Message From: Jonathan Worthington jonat...@jnthn.net Ovid wrote: It needs the timed fuse() from a Bomb role and a non-lethal explode() from a Spouse role, though each role provides both methods. I'm curious... 1) How often do you in real life find yourself needing to do things like this in real life? This is a sort of strained, if amusing, example. :-) We use roles very, very heavily. We've found that the important thing is choosing descriptive names. Thus, we rarely need to exclude methods from roles because our names are unambiguous. The only time I recall us running into this problem is when we have two or more methods with identical names which perform semantically identical behaviors but need different implementations. 2) A lot of me wonders if a need to exclude a method from a role is a hint that the role does too many things and should be decomposed into smaller pieces, such that it can be applied in a more granular way? As noted, we only have this happen when the semantics are identical but the implementation must differ. At this point, we really do need a way to exclude methods. This doesn't happen very often, but it's happened enough (probably about 10 times in our code base) that a convenient way of handling this would be useful. I'm curious to hear the experiences of Ovid and others working with roles a lot too. Is this a serious lacking in Perl 6's roles as currently specified, or something that, in being absent, makes people consider their design more? Knowing that will influence the solution we choose, which has options ranging from, yes, make a neat syntax for it through leave it out of the core, and if people want it enough it can be a CPAN module. Actually, the only serious concern I have (pardon me if you've heard this before) is how we silently discard a role's method if the class provides it. A digression is in order. Some of you know the background behind roles, but not everyone. The problem with classes is that they tend to have two competing uses. Classes are agents of responsibility (which tends to make them grow larger) and, via inheritance, are agents of code reuse (which tends to want classes to be smaller). These competing tendencies have been a source of much OO pain and roles decouple the behavioral reuse from class responsibility quite nicely. That being said, roles also have two competing uses (though they don't conflict as badly). As units of behavior, they provide the functionality your code needs. However, they can also serve as an interface. The behavioral/interface divide has already demonstrated a subtle tension in the use of roles in my work. For those of you who have seen the arguments about this rage on use.perl, my apologies :( Interface: if you are taking advantage of a role as an interface, it's quite useful to have your class provide one or more methods with an identical signature to the role and have the role's method silently ignored. Behavioral: if you are primarily relying on roles to provide behavior (as we do at the BBC), then silently discarding the role's behavior by providing a method of the same name in your class can lead to very confusing bugs. I've lost a lot of time debugging this behavior. I'd like to see something like this (or whatever the equivalent Perl 6 syntax would be): class PracticalJoke does Bomb does SomeThingElse { method fuse() but overrides { ... } } The overrides tells Perl 6 that we're overriding the fuse() method from either Bomb or SomeThingElse (or both). Otherwise, a warning or exception would be useful to prevent me from accidentally overriding needed behavior. Again, we've lost a huge amount of time debugging this behavior with Moose::Roles and I'd hate to have to do this again with Perl 6. Your mileage may vary :) Cheers, Ovid -- Buy the book - http://www.oreilly.com/catalog/perlhks/ Tech blog- http://use.perl.org/~Ovid/journal/ Twitter - http://twitter.com/OvidPerl Official Perl 6 Wiki - http://www.perlfoundation.org/perl6
Private methods in Roles (Was: Re: YAPC::EU and Perl 6 Roles)
Em Qua, 2009-07-08 às 12:49 -0700, Ovid escreveu: Behavioral: if you are primarily relying on roles to provide behavior (as we do at the BBC), then silently discarding the role's behavior by providing a method of the same name in your class can lead to very confusing bugs. I've lost a lot of time debugging this behavior. That's actually a tipping point, and I'm thinking we never conceptually extrapolated the use of Roles to a point that competing Roles in a composition are bringing methods to the class that are actually relevant to that roles, but doesn't mix well with the semantics of the composed class. Maybe what we need is a way to define methods that are not composed to the class at all, but are there just for implementation sake. That could probably mean that methods declared as privates in the role should not be composed in the class, and the lookup of private methods should honor the original place of declaration... daniel
Re: YAPC::EU and Perl 6 Roles
Ovid wrote: I'd like to see something like this (or whatever the equivalent Perl 6 syntax would be): class PracticalJoke does Bomb does SomeThingElse { method fuse() but overrides { ... } } The overrides tells Perl 6 that we're overriding the fuse() method from either Bomb or SomeThingElse (or both). Otherwise, a warning or exception would be useful to prevent me from accidentally overriding needed behavior. This would also be useful to catch the case where you mistype the override method, and so have to go debug why you're still using the base-class (or role) version of the method.
r27485 - docs/Perl6/Spec/S32-setting-library
Author: moritz Date: 2009-07-08 23:41:43 +0200 (Wed, 08 Jul 2009) New Revision: 27485 Modified: docs/Perl6/Spec/S32-setting-library/Numeric.pod Log: [S32/Num] log's base is positional Modified: docs/Perl6/Spec/S32-setting-library/Numeric.pod === --- docs/Perl6/Spec/S32-setting-library/Numeric.pod 2009-07-08 19:45:10 UTC (rev 27484) +++ docs/Perl6/Spec/S32-setting-library/Numeric.pod 2009-07-08 21:41:43 UTC (rev 27485) @@ -129,7 +129,7 @@ =item log - our Num multi method log ( Num $x: Num :$base = Num::e ) is export + our Num multi method log ( Num $x: Num $base = Num::e ) is export Logarithm of base C$base, default Natural. Calling with C$x == 0 is an error.
Re: YAPC::EU and Perl 6 Roles
On Wed, Jul 08, 2009 at 01:59:53PM -0700, Dave Whipp wrote: Ovid wrote: I'd like to see something like this (or whatever the equivalent Perl 6 syntax would be): class PracticalJoke does Bomb does SomeThingElse { method fuse() but overrides { ... } } The overrides tells Perl 6 that we're overriding the fuse() method from either Bomb or SomeThingElse (or both). Otherwise, a warning or exception would be useful to prevent me from accidentally overriding needed behavior. This would also be useful to catch the case where you mistype the override method, and so have to go debug why you're still using the base-class (or role) version of the method. Note we already have syntax that can be applied here: supersede method fuse {...} augment method fuse {...} It only remains to spec what those mean... :) Larry
Roles discussion
Hi all, It's been interesting to participate in the roles discussion so far, and I'm happy to see there's a lot of interest in getting the right options and the right defaults. I'm leaving tomorrow morning on vacation, and will be mostly offline for a week or so (the alps are quite a distraction...). So I just wanted to note that my lack of further comments for a while aren't at all lack of interest. Just so nobody is wondering. :-) Thanks! Jonathan
r27487 - docs/Perl6/Spec
Author: lwall Date: 2009-07-09 02:36:00 +0200 (Thu, 09 Jul 2009) New Revision: 27487 Modified: docs/Perl6/Spec/S14-roles-and-parametric-types.pod Log: [S14] a slightly closer approximation to eventual reality Modified: docs/Perl6/Spec/S14-roles-and-parametric-types.pod === --- docs/Perl6/Spec/S14-roles-and-parametric-types.pod 2009-07-08 22:20:01 UTC (rev 27486) +++ docs/Perl6/Spec/S14-roles-and-parametric-types.pod 2009-07-09 00:36:00 UTC (rev 27487) @@ -15,8 +15,8 @@ Created: 24 Feb 2009 (extracted from S12-objects.pod) -Last Modified: 26 Jun 2009 -Version: 7 +Last Modified: 8 Jul 2009 +Version: 8 =head1 Overview @@ -357,8 +357,8 @@ role xxx { has Int $.xxx; -multi trait_mod:is(::?CLASS $declarand where {!.defined}, xxx $trait, $arg?) {...} -multi trait_mod:is(Any $declarand, xxx $trait, $arg?) {...} +multi trait_mod:is(::?CLASS $declarand where {!.defined}, :$xxx!) {...} +multi trait_mod:is(Any $declarand, :$xxx!) {...} } Then it can function as a trait. A well-behaved trait handler will say @@ -370,19 +370,19 @@ matching, you can also say: class MyBase { -multi trait_mod:is(MyBase $declarand where {!.defined}, MyBase $base, $arg?) {...} -multi trait_mod:is(Any $declarand, MyBase $tied, $arg?) {...} +multi trait_mod:is(MyBase $declarand where {!.defined}, MyBase $base) {...} +multi trait_mod:is(Any $declarand, MyBase $tied) {...} } These capture control if CMyBase wants to capture control of how it gets used by any class or container. But usually you can just let it call the generic defaults: -multi trait_mod:is($declarand where {!.defined}, $base, $arg?) {...} +multi trait_mod:is($declarand where {!.defined}, $base) {...} which adds C$base to the isa list of class C$declarand, or -multi trait_mod:is(Any $declarand, $tied, $arg?) {...} +multi trait_mod:is(Any $declarand, $tied) {...} which sets the tie type of the container declarand to the implementation type in C$tied. @@ -395,8 +395,8 @@ Here's Cwill, which (being syntactic sugar) merely delegates to back to is: -multi sub trait_mod:will($declarand, $trait, arg) { -trait_mod:is($declarand, $trait, arg); +multi sub trait_mod:will($declarand, :$trait) { +trait_mod:is($declarand, :$trait); } Other traits are applied with a single word, and require special