On Fri, Mar 29, 2013 at 08:04:31AM -0700, Ovid wrote:
> ----- Original Message -----
>
> > From: Jesse Luehrs <[email protected]>
> > On Fri, Mar 29, 2013 at 05:34:50AM -0700, Ovid wrote:
> >> ----- Original Message -----
> >>
> >> > From: Lars Balker <[email protected]>
> >> >
> >> > 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