Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)

2009-07-13 Thread Ovid

- Original Message 
 From: Jon Lang datawea...@gmail.com

 Right.  But as they were originally conceived, they were interfaces
 that could also handle code reuse, rather than units of code reuse
 that could also be used as interfaces.  From this perspective, it
 makes perfect sense that a role's methods can be overridden as easily
 as they are.

As originally conceived in Perl 6 or in the original traits papers?  In the 
original research, the purpose of roles was to allow the decoupling of 
responsibility and behavior (code reuse) found in inheritance-based OO systems. 
 Traits (roles) took over code reuse.

 But you make a good point: there are some (a few? most?) programmers
 who are going to want to use roles primarily for code reuse, and who
 will want it to be a little more difficult to override the code
 provided by a role (e.g., requiring the use of supersede and perhaps
 augment in order to replace the definition with a new one).

Just to give people some real data to play with (our system may not be 
representative), here's some sample source code and some imformation about our 
use of roles in the BBC.

package PIPs::ResultSource::Series;
use Moose;
extends 'PIPs::ResultSourceBase::BrandSeries';
with qw(
PIPs::ResultSource::Role::DoesParentChildRelationships
PIPs::ResultSource::Role::DoesTags
PIPs::ResultSource::Role::DoesContentObject
PIPs::ResultSource::Role::DoesInspector
PIPs::ResultSource::Role::DoesRelatedLinks
PIPs::ResultSource::Role::DoesIdentifiers
PIPs::ResultSource::Role::DoesChangeEvents
);

 
(The astute reader will not that the base class is awful, but it's been a long, 
hard slog to get this far).

Most of our classes which implement roles have similar preambles, but with 
different behaviors listed.

Other points of interest.  Only 11 of 114 classes which implement roles exclude 
any methods (none use method aliasing) and we currently have 40 roles 
implemented.  

Only three classes provide methods which override role's methods, but in the 
few cases they do, we explicitly exclude the methods from the role to make it 
clear that we need to do this.  We had more overriding of role's methods, but 
continual refactoring has pushed those into roles.

 
So we're very, very heavily on the use roles for shared behavior side.  The 
relative paucity of overridden role methods suggests to me that (for our code), 
the annoyance of having to be explicit for overridding a role's methods easily 
offset by how hard it's been to debug this issue.  That being said, the pain in 
debugging might have been a side effect of the fast transformation from a 
complex inheritance hierarchy to a roles-based system.

Cheers,
Ovid
--
Buy the book - http://www.oreilly.com/catalog/perlhks/
Tech blog- http://use.perl.org/~Ovid/journal/
Twitter  - http://twitter.com/OvidPerl
Official Perl 6 Wiki - http://www.perlfoundation.org/perl6


RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)

2009-07-12 Thread Daniel Ruoso
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



Re: RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)

2009-07-12 Thread David Green

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: RFC: overriding methods declared by roles (Was: Re: Reusing code: Everything but the kitchen sink)

2009-07-12 Thread Jon Lang
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