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

Reply via email to