> From: Jesse Luehrs <d...@tozt.net>
>No, overriding is a perfectly valid way to resolve conflicts, and always >has been. alias and excludes are only important to fix cases where this >isn't possible, and this change should remove the last of those. So I can avoid spamming the list regarding things already hashed out, can you point me to the prior discussions where this change was decided? I'm hoping to better understand other's thinking on this (I've discussed this with mst, but I don't think it would be fair to assume that his opinions apply to everyone else). My concern about removing "alias" and "excludes" stems in part from how I appear to have a slightly different philosophy about OO programming. I view objects as experts about solving a given problem, but the underlying object is composed of "legos" of roles. Need some new behavior? I rummage around in my toy box looking for the legos to snap onto my existing object. Viewing each object as a collection of well-defined behaviors rather than "how can I squeeze this into a tree or graph hierarchy" lets me just get things done, though I realize I may be in the minority here. For example, in my first programming job, I worked several hours a week as a programmer and the rest of the week I was a glorified clerk. I was (illegally) paid at different rates for each job. So the "employee" sometimes behaves as a clerk and sometimes behaves as a programmer. Calculating my pay with roles is fairly straightforward (funky formatting to make it easier to fit in the email): { package R::Programmer; use Moose::Role; sub pay_rate {12} } { package R::Clerk; use Moose::Role; sub pay_rate {10} } { package R::Employee; use Moose; with 'R::Programmer' => { # this is why I want a "rename" property here exclude => 'pay_rate', alias => { pay_rate => 'programmer_pay_rate' } }, 'R::Clerk' => { exclude => 'pay_rate', alias => { pay_rate => 'clerk_pay_rate' } }; has [qw/hours programming_hours drudgery/] => ( is => 'ro' ); sub pay_rate {8} # ugly hack for quick example. Oops sub paycheck { my $self = shift; return $self->programmer_pay_rate * $self->programming_hours + $self->clerk_pay_rate * $self->drudgery; } } my $employee = R::Employee->new( programming_hours => 15, drudgery => 25, ); say $employee->paycheck; (yes, there's some cruft in this quickly hacked together example and it's deliberately kept simple (e.g., pay_rate) to focus on the role composition issue) How do I do that without roles? I'm not going to fall back on MI because that introduces the very problems that roles were trying to solve! Thus, delegation seems to be the trick (if I'm creating a straw man argument here, my apologies. Please correct me). Here's the first pass at that: { package R::Paycheck; use Moose::Role; requires 'pay_rate'; has [qw/hours_worked/] => ( is => 'ro' ); sub paycheck { my $self = shift; return $self->hours_worked * $self->pay_rate; } } { package C::Programmer; use Moose; with 'R::Paycheck'; sub pay_rate { 12 } } { package C::Clerk; use Moose; with 'R::Paycheck'; sub pay_rate { 10 } } { package C::Employee; use Moose; has [qw/programming_hours drudgery/] => ( is => 'ro' ); has 'programmer' => ( is => 'ro', lazy => 1, builder => '_build_programmer' ); has 'clerk' => ( is => 'ro', lazy => 1, builder => '_build_clerk' ); sub _build_programmer { my $self = shift; C::Programmer->new( hours_worked => $self->programming_hours ); } sub _build_clerk { my $self = shift; C::Clerk->new( hours_worked => $self->drudgery ); } sub paycheck { my $self = shift; return $self->programmer->paycheck + $self->clerk->paycheck; } } In some respects it's cleaner, but in other respects, it's not. For example, many of my roles are things that shouldn't necessarily be instantiated on their own (Serializable), but if I'm forced to use delegation for composition because I don't have exclusion and aliasing, then sometimes I'm going to have to fall back on the old hack of creating classes to add some behavior, even if the "class" shouldn't really be a class. (See also: http://steve-yegge.blogspot.fr/2006/03/execution-in-kingdom-of-nouns.html). Further, while I realize my "lego" approach to building classes isn't to everyone's tastes, it does tend to shake out bugs in role implementations very quickly. For example, I've had to give up on Moo because its roles are so buggy (https://rt.cpan.org/Public/Bug/Display.html?id=82711). So unless there's a really concrete, demonstrable problem with aliasing and excluding, I'm very concerned about taking away these tools that have been in my toolbox for many years. Finally, we can consider performance. When a role's methods are flattened into my class, they're going to be faster than delegation. use Benchmark; my %args = ( programming_hours => 15, drudgery => 25, ); timethese( 100000, { roles => sub { R::Employee->new(%args)->paycheck }, classes => sub { C::Employee->new(%args)->paycheck }, } ); In this example, we see that roles are more than twice as fast as delegation: Benchmark: timing 100000 iterations of classes, roles... classes: 14 wallclock secs (14.20 usr + 0.00 sys = 14.20 CPU) @ 7042.25/s (n=100000) roles: 6 wallclock secs ( 5.63 usr + 0.00 sys = 5.63 CPU) @ 17761.99/s (n=100000) As it turns out, object creation is very expensive, so if we skip that part and just call the salary method (and we'll bump this up to one million iterations): use Benchmark; my %args = ( programming_hours => 15, drudgery => 25, ); my $roles = R::Employee->new(%args); my $classes = C::Employee->new(%args); timethese( 1000000, { roles => sub { $roles->paycheck }, classes => sub { $classes->paycheck }, } ); Benchmark: timing 1000000 iterations of classes, roles... classes: 3 wallclock secs ( 2.88 usr + 0.00 sys = 2.88 CPU) @ 347222.22/s (n=1000000) roles: 0 wallclock secs ( 1.28 usr + 0.00 sys = 1.28 CPU) @ 781250.00/s (n=1000000) We still see the roles as slightly twice as fast as delegation. In short: 1. Allowing a role to silently override a consumed role's methods moves Perl roles even further away from Smalltalk-style traits and its associative and commutative guarantees. Regarding plans to remove 'alias' and 'excludes': 2. Intending to remove the aliasing and excluding composition tools removes tools that allow finer control over how you consume your objects. 3. Removing certain use cases for role composition and forcing developers to fall back on delegation is a significant performance hit. Cheers, Ovid-- Twitter - http://twitter.com/OvidPerl/ Buy my book - http://bit.ly/beginning_perl Buy my other book - http://www.oreilly.com/catalog/perlhks/ Live and work overseas - http://www.overseas-exile.com/