On Fri, Mar 29, 2013 at 08:04:31AM -0700, Ovid wrote: > ----- Original Message ----- > > > From: Jesse Luehrs <d...@tozt.net> > > On Fri, Mar 29, 2013 at 05:34:50AM -0700, Ovid wrote: > >> ----- Original Message ----- > >> > >> > From: Lars Balker <l...@balker.dk> > >> > > >> > As Jesse's earlier example showed, instead of excluding and > > aliasing, > >> > just refer to the methods directly within the roles: > >> > > >> > use v5.10.0; > >> > { 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'; > >> > with 'R::Clerk'; > >> > has [qw/programming_hours drudgery/] => ( is => 'ro' > > ); > >> > sub paycheck { > >> > my $self = shift; > >> > return $self->R::Programmer::pay_rate * > > $self->programming_hours > >> > + $self->R::Clerk::pay_rate * $self->drudgery; > >> > } > >> > } > >> > my $employee = R::Employee->new( > >> > programming_hours => 15, > >> > drudgery => 25, > >> > ); > >> > say $employee->paycheck; > >> > >> > >> With 'R::Programmer' and 'R::Clerk' composed on separate > > lines, you no > >> longer get compositional safety and roles become little more than > >> glorified mixins. These were problems that roles/traits were designed > >> to fix. This is not an improvement. > > > > There's no need to actually compose them separately, things work just > > fine if you compose them on the same line. > > I think you may have misread the code. He composed them separately because if > he composed them at the same time, he would get a conflict: > > Due to a method name conflict in roles 'R::Clerk' and 'R::Programmer', > the method 'pay_rate' must be implemented or excluded by 'R::Employee' at > ... > > Thus, in this instance, he was forced to compose those roles > separately (since "exclude" is going to be taken away) and lose > compositional safety.
I did misread. In this case though, this is something that you shouldn't even be allowed to do in the first place, and the fact that Moose allows it is arguably a bug. You're consuming a role which provides a pay_rate method, but then you aren't providing a pay_rate method yourself. This entirely breaks the concept of a role interface as a contract. > >> And I should not have to hardcode the class names into my method > >> names. That's a hack that we sometimes fall back on to work around MI > >> limitations when you're trying to call an otherwise unreachable method > >> and now it's bubbling up into roles? > > > > How is this meaningfully different? You're already hardcoding the role > > names at the point where you consume them. > > > For the same reason that any cutting-and-pasting is a bad idea: our > role as programmers is to minimize code duplication as much as > possible to ensure that when we do have to change something, we > minimize the number of places where said change needs to be made. I > would much rather switch the name of the role I'm using at the top of > my class and know that things *just work* (again, as was part of the > original intent of Smalltalk traits) instead of having to remember to > do a search and replace for every place where I've had to hard-code a > package name. > > I could also easily see someone subclassing "Employee" and now having > to remember that if they want pay_rate, it's > either $self->R::Clerk::pay_rate or $self->R::Programmer::pay_rate . > The subclass *shouldn't* have to know the implementation details of > Employee to make this work. Piers' solution of using > "*programmer_pay_rate = \&R::Programmer::pay_rate is" is ugly, but > it's marginally better. However, that's the sort of scaffolding code > that roles were (pardon the broken record) designed to handle. The > tools to get this right are already in Moose, so I'm really confused > about what's going on here. You shouldn't ever need to access the individual implementations of R::Clerk::pay_rate or R::Programmer::pay_rate except from within the role or class that composes them. Doing otherwise also defeats the purpose of a role as an interface contract. If you want to provide that information, you should write it as an actual method sub programmer_pay_rate { shift->R::Programmer::pay_rate } > I ask again: what benefit from removing 'alias' and 'exclude' is so > strong that it's worth removing a tool that Moose has provided for > years? Every example you've given so far seems to be an abuse of the role system. I still have yet to see an example where alias or excludes is used in a way that makes sense (and as I mentioned before, I have never once had a reason to use alias or excludes except to work around the issue that is fixed in Moose 2.08). -doy