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