On Sat, Oct 22, 2011 at 8:25 AM, Anthony Ferrara <ircmax...@gmail.com>wrote:

> Well, I have a few opinions on this that I think are worth sharing:
>

Anthony, thanks for your reply.

1. If this change is made, we no longer would have mixins, but would
> have regular multiple-inheritance (With all the "issues" associated
> with it).  Note that I said mixins and not traits.  That's because
> since these were implemented with state (member properties), they are
> not traits as implemented, but regular mixins. (Yet another gripe, but
> that's slightly off topic).
>

While this may be scary to many it is in fact the motivation for the
feature.  Multiple inheritance or some semblance thereof is something
user-space folks have been interested in for some time [1].  There is no
unanimous interest for it, nor is there unanimous favor for practically
anything in PHP, that's what makes it unique IMO, different strokes for
different folks.


> 2. Traits and mixins are supposed to be the symmetric counterpart to
> interfaces.  An interface is a specification without implementation.
> A trait/mixin is supposed to be implementation without specification.
> And a class (including abstract) is implementation with specification.
>  So if you allowed type hinting of traits/mixins, wouldn't that just
> make them nothing more than abstract classes (in that they can't be
> used directly, but both provide the same info)?
>

True, but doesn't the use of the abstract keyword in a trait already mean
they support specification?  There may be an academic concept of a trait,
but perhaps it has already been massaged for the PHP implementation.


> There's a much bigger problem though.  How would you solve the problem
> where a class uses a trait that implements an interface, but you don't
> pull one of the methods through (for example, you exclude it in the
> use clause)?  At first this sounds simple, just enforce it on the
> class.  But that means that the compiler couldn't enforce the
> interface at definition time of the trait.  So then that means that by
> the trait implementing the interface is actually *forcing* any class
> that implements it to be compliant and ensure that it implements the
> interface.  So at that point the trait stops being useful for
> horizontal reuse, but becomes nothing more than a class with the
> limitations that brings along.  Traits/mixins were designed to avoid
> this problem.  So why should we re-introduce it?
>

I'm sorry I'm missing the point here, could you show a brief sample?  I
think the *forcing* aspect is the point here though, and it is desired.  A
trait that uses several methods from an interface, say Iterator or another
class like SplFileInfo.  The trait should be able to say it needs a subset
of the methods in that interface or class today, and may at any time in the
future rely on any of the full set of methods defined in the interface or
class.  Again, this functionality is already available in the current
implementation via the abstract keyword.  It just seems silly to have to
maintain a list of methods by hand that could be done with a concise
notation instead.  And it's even uglier if the trait relies on more than one
class interface, imagine code like this:

<?php
trait IteratorHelper {
/* Iterator */
abstract public function current();
abstract public function key();
abstract public function next();
abstract public function rewind();
abstract public function valid();

/* SplFileInfo */
abstract public function getATime();
abstract public function getBasename();
// etc..
}
?>

The author is wondering, do I bother to key in all the methods from
SplFileInfo now in case I ever use one of the methods I haven't in the
original implementation, or wait until I do use another method from
SplFileInfo and then update the list of abstract methods.  And what if I
have an abstract method mentioned in the list, but remove calls to it from
within the trait and then forget to update the list, is that bad?  Also note
the ambiguity introduced without comments indicating which foreign class /
interface these abstract (required) methods come from.  The comments here,
and indeed the feature I suggest, would be more expressive to authors
because it would be explicit.  Imagine encountering the code segment above
with the absence of the comments, you would be good to guess which classes /
interfaces the trait was intended to be used with.  Instead something like:

<?php
trait IteratorHelper require Iterator, SplFileInfo {
//..
}
?>

is not only more concise, it is more expressive IMO in that it more clearly
indicates the author's intent for the use of said trait.  It is also more
maintainable because there is no need to maintain a list of methods (for
which there is no explicit way to describe the interface / class they belong
to) when changes are made.  There is also value for documentation tools.
 With the current paradigm of just the abstract keywords using comments as I
suggested is not something a documentation tool can pick up, though I'm sure
it will be a matter of time until those tools cook up a silly annotation to
support it...


> Some of that is design choice, but I think we need to realize that
> there are a lot of people who have thought about these problems before
> in other areas of CS.  What do other languages do?  Can you name one
> mainstream language that implements traits/mixins as a first class
> citizen where they can implement an interface or can be type-hinted
> against?  In fact, the original paper the RFC references
> (http://scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf)
> explicitly recommends separating the specification (interface) from
> the trait:
>
> A quote from that paper:
>
> > We solved these problems by creating traits for the different collection
> properties and
> > combining them to build the required collection classes. To achieve
> maximum flexibility,
> > we separated the properties specifying the implementation of a collection
> from the
> > properties specifying the interface. This allowed us to freely combine
> different interfaces (e.g.,
> > “sorted-extensible interface” and “sorted-extensible-immutable
> interface”) with any of the
> > suitable implementations (e.g., “linked-list implementation” and
> “array-based implementation”).
>
> So in all, I feel it would be a significant mistake to allow traits
> (mixins) to implement interfaces or be type-hinted against.  It goes
> against the principles that they were designed with and destroys some
> of their key benefits...
>

A couple thoughts here.

1. I looked around in other language specs before re-hashing this topic and
from what I can tell these other languages don't have a concept of
interfaces as PHP does.  One could say they do have classes proper and did
not support traits noting the classes which they were dependent upon in
their specification.  Seems to me many of these other languages were
more wholly conceived at their inception and I doubt the user base had been
striving to implement multiple inheritance via a workaround for the past 5
years.  Just google 'php multiple inheritance'.

2. For better or for worse the decision to support specification within
traits has already been made in the form of the abstract keyword.  I'm
suggesting taking it one step further for folks who are interested in some
semblance of multiple inheritance.

If it's a nightmare to implement internally maybe it's best left alone, but
if it is straightforward I say implement it and let folks take on 'all the
problems of multiple inheritance' if they wish to; everyone else can just
laugh at them if they wish to.  Isn't that the PHP way?

[1] http://www.issociate.de/board/post/478936/Multiple_Inheritance.html

thanks,

-nathan

Reply via email to