[RFC] Traits with interfaces

php.internals

Kevin Gessner

10 years ago
Hello internals team! I'd like to propose an RFC to allow traits to implement interfaces. I've noticed s pattern in Etsy's code and elsewhere, where a trait provides a common implementation of an interface. Classes that use the trait are required to also explicitly declare the interface to benefit. I propose that traits be permitted to declare and implement interfaces. Classes that use such a trait would then implement the interface, as though it were declared on the class, without declaring the interface explicitly. I believe this small change would be a useful improvement to the OO system. I've written a draft RFC, but I don't have karma to create the wiki page for it. Could someone please grant wiki karma to my account, kevingessner? I don't yet have an implementation, but I'll be starting on one once I've posted the RFC. I look forward to your thoughts and feedback. Thanks in advance -- Kevin Kevin Gessner Staff Software Engineer etsy.com

Sebastian Bergmann

10 years ago
Am 17.02.2016 um 15:25 schrieb Kevin Gessner:
> Hello internals team! I'd like to propose an RFC to allow traits to > implement interfaces.
I think that would violate "The Flattening Property" [1], meaning that the fact that a class uses a trait must not be noticable by a user of that class. Also bear in mind that explicit is better than implicit. Two explicit declarations, "implements <interface>" and "uses <trait>", makes it obvious from looking at the code of a class that is provides a specific API and uses a trait for its implementation.
-- [1] https://wiki.php.net/rfc/horizontalreuse

Chase Peeler

10 years ago
I agree. with Sebastian. I'm personally a big fan of using traits as the implementation mechanism for interfaces (i.e. implement the interfaces methods in a trait, then use that trait in a class that implements the interface), but I don't think there is anything positive to gain from having the trait actually implement the interface. On Wed, Feb 17, 2016 at 9:34 AM Sebastian Bergmann <sebastian@php.net> wrote:
> Am 17.02.2016 um 15:25 schrieb Kevin Gessner: > > Hello internals team! I'd like to propose an RFC to allow traits to > > implement interfaces. > > I think that would violate "The Flattening Property" [1], meaning > that the fact that a class uses a trait must not be noticable by a > user of that class. > > Also bear in mind that explicit is better than implicit. Two > explicit declarations, "implements <interface>" and "uses <trait>", > makes it obvious from looking at the code of a class that is > provides a specific API and uses a trait for its implementation. > > Exactly. Not to mention that interfaces are "types" (i.e. $object
instanceof MyInterface will be true if $object is an instance of a class that implements MyInterface) while traits are not. By moving the "implements MyInterface" into the trait, you "hide" and important attribute of the class that should be obvious and explicit.
> -- > [1] https://wiki.php.net/rfc/horizontalreuse > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --
-- Chase chasepeeler@gmail.com

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 9:34 AM, Sebastian Bergmann <sebastian@php.net> wrote:
> Am 17.02.2016 um 15:25 schrieb Kevin Gessner: > > Hello internals team! I'd like to propose an RFC to allow traits to > > implement interfaces. > > I think that would violate "The Flattening Property" [1], meaning > that the fact that a class uses a trait must not be noticable by a > user of that class. >
I don't believe that it will violate that property. Take this code for example, which would be valid with the proposed change: <?php interface I { function foo(); } trait T implements I { function foo() { } } class C { use T; } print_r(class_implements(C::class)); // Array // ( // [I => I] // ) Given only class C, an outside caller would not be able to distinguish it from a class that implements I directly, or from a class that implements I via inheritance. Is there an additional wrinkle here that I've not considered?
> Also bear in mind that explicit is better than implicit. Two > explicit declarations, "implements <interface>" and "uses <trait>", > makes it obvious from looking at the code of a class that is > provides a specific API and uses a trait for its implementation. >
I consider this sort of implicit declaration a benefit, in the spirit of DRY. It's in the same vein that an interface can be implicitly implemented by way of a class's superclass, which reduces code duplication and extra declarations in subclasses. And in fact it makes the trait itself more explicit: if the intention of the code is that a trait provides an implementation of a particular interface (I've found several in-the-wild examples of this, such as [1][2]), all the better that the trait explicitly declares that interface. 1: https://github.com/symfony/symfony/blob/582f4753a343f230fbe18b4e9a0747d48351ddfb/src/Symfony/Component/DependencyInjection/ContainerAwareInterface.php 2: https://github.com/symfony/symfony/blob/582f4753a343f230fbe18b4e9a0747d48351ddfb/src/Symfony/Component/DependencyInjection/ContainerAwareTrait.php

Levi Morrison

10 years ago
On Wed, Feb 17, 2016 at 7:25 AM, Kevin Gessner <kgessner@etsy.com> wrote:
> Hello internals team! I'd like to propose an RFC to allow traits to > implement interfaces. > > I've noticed s pattern in Etsy's code and elsewhere, where a trait provides > a common implementation of an interface. Classes that use the trait are > required to also explicitly declare the interface to benefit. I propose > that traits be permitted to declare and implement interfaces. Classes that > use such a trait would then implement the interface, as though it were > declared on the class, without declaring the interface explicitly.
I am unsure on the particular detail of the class automatically implementing the interface. I want to add my personal experience with traits: every time I create a trait it is to implement an interface. Here is a publicly available [example with OuterIterator](https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/OuterIteratorTrait.php): trait OuterIteratorTrait { abstract function getInnerIterator() : Iterator; function current() { return $this->getInnerIterator()->current(); } function next(): void { $this->getInnerIterator()->next(); } function key() { return $this->getInnerIterator()->key(); } function valid(): bool { return $this->getInnerIterator()->valid(); } function rewind(): void { $this->getInnerIterator()->rewind(); } } This makes it really easy to implement iterators. The class that uses the trait can simply implement `getInnerIterator` and then modify only the methods that differ from standard behavior. Here are a few examples: * https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/HashMapIterator.php * https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/HashSetIterator.php * https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/SortedMapIterator.php * https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/SortedSetIterator.php I can see how it would be nice to allow the trait to officially declare that it implements some interface (in this case Iterator or OuterIterator) which would require that the trait has fully implemented the required methods (or declared them as abstract). This would be a small improvement but helpful. I am less certain about the classes which `use` it automatically inheriting the interfaces. To clarify: I am neither in favor or against that part. Or at least at this stage, anyway.

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 10:26 AM, Levi Morrison <levim@php.net> wrote:
> I want to add my personal experience with traits: every time I create > a trait it is to implement an interface. Here is a publicly available > [example with OuterIterator]( > https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/OuterIteratorTrait.php > ): > > [snip] > > I can see how it would be nice to allow the trait to officially > declare that it implements some interface (in this case Iterator or > OuterIterator) which would require that the trait has fully > implemented the required methods (or declared them as abstract). This > would be a small improvement but helpful. > > I am less certain about the classes which `use` it automatically > inheriting the interfaces. To clarify: I am neither in favor or > against that part. Or at least at this stage, anyway. >
I don't think there is enough benefit from allowing traits to declare interfaces, but not propagating the interface out to classes that insert the trait. It does provide an extra bit of enforcement on the trait's methods, but the real value is in making that contract a part of the classes. In what situation would a class that inserts OuterIterator not want to declare the Iterator class? If every class that inserts the trait would also declare Iterator, it's needless repetition to require that both the class and the interface. I assert that there is no useful case where a class would not want an interface declared by one of its traits, and this RFC proposes the same.

Levi Morrison

10 years ago
On Wed, Feb 17, 2016 at 12:48 PM, Kevin Gessner <kgessner@etsy.com> wrote:
> On Wed, Feb 17, 2016 at 10:26 AM, Levi Morrison <levim@php.net> wrote: >> >> I want to add my personal experience with traits: every time I create >> a trait it is to implement an interface. Here is a publicly available >> [example with >> OuterIterator](https://github.com/morrisonlevi/Ardent/blob/master/src/Collection/OuterIteratorTrait.php): >> >> [snip] >> >> I can see how it would be nice to allow the trait to officially >> declare that it implements some interface (in this case Iterator or >> OuterIterator) which would require that the trait has fully >> implemented the required methods (or declared them as abstract). This >> would be a small improvement but helpful. >> >> I am less certain about the classes which `use` it automatically >> inheriting the interfaces. To clarify: I am neither in favor or >> against that part. Or at least at this stage, anyway. > > > I don't think there is enough benefit from allowing traits to declare > interfaces, but not propagating the interface out to classes that insert the > trait. It does provide an extra bit of enforcement on the trait's methods, > but the real value is in making that contract a part of the classes.
I do agree that there is only a little benefit but I disagree that it is "not enough". It's a quality of life change to an existing feature that has no impact outside of that feature – sounds perfect to me.
> In what situation would a class that inserts OuterIterator not want to > declare the Iterator class? If every class that inserts the trait would > also declare Iterator, it's needless repetition to require that both the > class and the interface.
Funny you should ask, because OuterIteratorTrait implementing OuterIterator is actually a case where not all using classes will want to be OuterIterators formally (just get the horizontal code reuse). This is because I don't want to "leak" the inner iterator because it is suppose to be abstracted. In these cases I use the syntax for using a trait method under a different visibility.

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 6:13 PM, Levi Morrison <levim@php.net> wrote:
> On Wed, Feb 17, 2016 at 12:48 PM, Kevin Gessner <kgessner@etsy.com> wrote: > > I don't think there is enough benefit from allowing traits to declare > > interfaces, but not propagating the interface out to classes that insert > the > > trait. It does provide an extra bit of enforcement on the trait's > methods, > > but the real value is in making that contract a part of the classes. > > I do agree that there is only a little benefit but I disagree that it > is "not enough". It's a quality of life change to an existing feature > that has no impact outside of that feature – sounds perfect to me. > > > In what situation would a class that inserts OuterIterator not want to > > declare the Iterator class? If every class that inserts the trait would > > also declare Iterator, it's needless repetition to require that both the > > class and the interface. > > Funny you should ask, because OuterIteratorTrait implementing > OuterIterator is actually a case where not all using classes will want > to be OuterIterators formally (just get the horizontal code reuse). > This is because I don't want to "leak" the inner iterator because it > is suppose to be abstracted. In these cases I use the syntax for using > a trait method under a different visibility. >
Thanks, Levi, that makes sense. In my existing RFC, the case of changing the visibility of an included method would cause a fatal error (as the including class would no longer fulfill the interface), but that's not the only reasonable behavior. I can make this an open question in the RFC. It sounds like this summarizes most people's feelings at this point: adding interfaces to trait declarations is a reasonable change, but propagating the interfaces out to classes is contentious. I'm leaning at this point toward splitting the RFC ( https://wiki.php.net/rfc/traits-with-interfaces) into two proposals: 1. Allow a trait to declare one or more interfaces via the `implements` keyword on the trait definition. The engine will enforce that the trait's methods fulfill the interface. 2. A class, by including a trait that declares interfaces, will implicitly be declared to implement those interfaces. The vote on 2 would be moot if the vote on 1 does not pass. The combination of these is the existing RFC as a single proposal; I'd keep it as a single RFC with two proposals and two voting sections, voted separately but concurrently. It looks from history like multiple voted proposals on one RFC is OK, but please let me know if I'm violating protocol in some way. Would folks be amenable to that change to the RFC? Cheers -- Kevin

Markus Fischer

10 years ago
Hello, On 17.02.16 15:25, Kevin Gessner wrote:
> I've noticed s pattern in Etsy's code and elsewhere, where a trait provides > a common implementation of an interface. Classes that use the trait are > required to also explicitly declare the interface to benefit. I propose > that traits be permitted to declare and implement interfaces.
I like this, because this is a pattern I experienced myself. In my code I've: - interface GroupBulkFilterable { .. } - trait GroupBulkFilterTrait { .. } and later class AbstractModelBridge implements ModelBridge, GroupBulkFilterable { use GroupBulkFilterTrait; Would use the interface on the trait in an instant.
> Classes that > use such a trait would then implement the interface, as though it were > declared on the class, without declaring the interface explicitly.
I don't like this. I prefer explicit over implicit. I would still see value in the first point only (but haven't thought that through ...). cheers, - Markus

Arne Blankerts

10 years ago
Hi Kevin,   Hi all, On Mi, 2016-02-17 at 09:25 -0500, Kevin Gessner wrote:
>  > I've noticed s pattern in Etsy's code and elsewhere, where a trait > provides a common implementation of an interface.  Classes that use > the trait are required to also explicitly declare the interface to > benefit.  I propose that traits be permitted to declare and implement > interfaces.  Classes that use such a trait would then implement the > interface, as though it were declared on the class, without declaring > the interface explicitly.
While I do see the benefit in that behavior and I admit I sometimes in the past wished for something like that as well, it's quite implicit as one cannot see that class "implements" the interface. Which makes it already less appealing to me. Additionally, I wonder what the expected behavior would be when it comes to aliasing, visibility changing and conflict resolving? Kind regards, Arne Blankerts
-- Those who do not understand Unix are condemned to reinvent it, poorly (Henry Spencer,1987)

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 2/17/2016 3:25 PM, Kevin Gessner wrote:
> I've noticed s pattern in Etsy's code and elsewhere, where a trait > provides a common implementation of an interface. Classes that use > the trait are required to also explicitly declare the interface to > benefit. I propose that traits be permitted to declare and > implement interfaces. Classes that use such a trait would then > implement the interface, as though it were declared on the class, > without declaring the interface explicitly.
I agree with the others having a class implementing the interface automatically just because a trait implements it is not a good idea. The class should always decide on its own if it wants to implement an interface or not. Note that the situation with true inheritance (extends) is very different compared to the usage of a trait. When one extends a class it automatically agrees to the "is a" contract. In other words, it MUST implement all interfaces that any of the parents implements, otherwise it cannot fulfill the "is a" contract. Another thing that is important to note is that a class might want to use a particular trait but does not want to implement the interface in the first place. That being said, the actual thing one wants is that the IDE helps us to not overlook a method that we might forgot about while implementing that default trait for an interface. This is something where PHPDoc shines and should be used for. Actually I proposed this already for the upcoming PSR-5 that covers PHPDoc. However, that feature might be out of scope for the PSR. TL;DR Create feature requests for PHPDoc software (IDEs etc.) to support "@implements InterfaceName" (and maybe "@extends ClassName") in order to fulfill this use case and do not change the language. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWxLkuAAoJEOKkKcqFPVVrJe4P/1Qz43ey6g7aEQUegOyfuoIQ GokTsUS42pedbGr3mYdf2/qatFp+/CyMv4FovLX4+m2wRn10AFmeYENi4RcFItb+ ILiiTynMPrQqV3IGgujHYwNDA/J4Qo3mn8FnNcLWl6nz3bFaxPTu2ygEgPYWz9Yu Y9qBcPqdQn1o/ipUf9D+thFzDKtCqCAEoPmBcCQWwCIK3yiUfqbkOzNBqynlE/qK 9lhCrF3+5XSrTkxl+gwzm9YUEA/EO7BUO6i8bXex9ZPDBSfRQet29DYUR9pGu8/p VMyHd9w1H60QCequreW4kZwixO010jKAO9EUwXMU/+yYV5C2Iy/5eRcKVERgLT75 BtKC0eNRW6kw58ZnRRYiVLfqlCZDQYU2kQ0S7R4Nym04agOZ2tdneKFgEYn3uo/k qNzaTLLYRbPNnfyYcZ4DRtsI82OIHrw2x8U9KrOU48fnvtLeW3+nShwXFK1oLU40 hsB+jU28OvsSohBngwAQn2z7nIJVqLiyVCnWj48omtzBJ7GesfIUt2Cu1cG9yK+/ EeCVIXhI9m1W5Wf2k8oEaB8Amy5rZwW6b1PA+JHJ+aYQjvG3CY6ipSka/RhRqAgf ABTLX8k1a7Hdd8kXheC1vIhhjLgIWijW59Bzg/oxN4RiRe2c90s4e857VN+fEBfK 6xVA5XfwMQ2N4tn+P2Zq =7rF9 -----END PGP SIGNATURE-----

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 1:17 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> I agree with the others having a class implementing the interface > automatically just because a trait implements it is not a good idea. > The class should always decide on its own if it wants to implement an > interface or not. >
I'm not clear on the motivation for this principle. Is there anything I could read up about explicit vs implicit in PHP design?
> > Note that the situation with true inheritance (extends) is very > different compared to the usage of a trait. When one extends a class > it automatically agrees to the "is a" contract. In other words, it > MUST implement all interfaces that any of the parents implements, > otherwise it cannot fulfill the "is a" contract. > > Another thing that is important to note is that a class might want to > use a particular trait but does not want to implement the interface in > the first place. >
What cases are you thinking of here? I'm trying to come up with an example where a class that wouldn't want an interface provided by a trait it uses.
> > That being said, the actual thing one wants is that the IDE helps us > to not overlook a method that we might forgot about while implementing > that default trait for an interface. This is something where PHPDoc > shines and should be used for. Actually I proposed this already for > the upcoming PSR-5 that covers PHPDoc. However, that feature might be > out of scope for the PSR. > > TL;DR Create feature requests for PHPDoc software (IDEs etc.) to > support "@implements InterfaceName" (and maybe "@extends ClassName") > in order to fulfill this use case and do not change the language. >
I don't use a PHPDoc-aware IDE (I'm old fashioned), so I'd want any change to be part of the language itself, where enforcement is guaranteed.

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 2/17/2016 9:01 PM, Kevin Gessner wrote:
> > I'm not clear on the motivation for this principle. Is there > anything I could read up about explicit vs implicit in PHP design? >
This is not so much about implicit vs. explicit PHP design for me. This is about inheriting something from a piece of "reusable dead code" (that is how I use to explain traits to youngsters). On 2/17/2016 9:01 PM, Kevin Gessner wrote:
> > What cases are you thinking of here? I'm trying to come up with an > example where a class that wouldn't want an interface provided by a > trait it uses. >
In any case in which you want to provide the same functionality as the trait provides but to not want to be of the same kind. On 2/17/2016 9:01 PM, Kevin Gessner wrote:
> > I don't use a PHPDoc-aware IDE (I'm old fashioned), so I'd want any > change to be part of the language itself, where enforcement is > guaranteed. >
That is unfortunate for you because you are missing out on many great features and that was even the case in the 1990s. However, I am not here to convince you of using an IDE. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWxNZ7AAoJEOKkKcqFPVVrNh0P/2126wunMnqX1g698s3T1g2R olOzE3+tRLN8shmwl3/Gk2d8OoW4vsx5D0et/w2T2X0rIIBm2Fq7kRfQU5kmVL0x zTZkYL5oDxlasGNjlFqhJhPxILbrzrrSRK15DsGHOzau+hCdlXjK+Cs+UuXXzw4h Wk4Mbak16sWX4nWVdotB7otHrXNQCVAwyNuEfTIWEWVKBGzAzr8iMgmiaepgtUoT KclQOHdOHC1J2+16MiEK+P1wQZf/SMh2SecxFLBm/xRNerHXBYPM3yD9DDmDg+B7 K3BhRjI8fa45Xo7lQFxF9BI30IdupaJAYguPJeL7/w+NggiY0iq+5m/smrDEjrTz 01GmjN/2GTlN6xOTHXbnc+FdL7RTbGupmBBNXMJrjyxlFkjRFnDB0LxU3KsZj8MB RohVPmsZEqhlmGTXHSrf46Ps1AwPafheJDkXz4HASye+54umg0TIxPS3lzra+KIt 0a7O53t4NYvP1/Q1pwIg8iM+atrHmb5D55mHtzB4Z/zz/d0cIw6gO/arJAIgBO+S 10VlFnK1rPKVlUj0lk2kPEzMTmBQF6pYbjnnJoLuCID7UsYjABxYval6yT1yblSu zKcU0UXtlR6rJA9fN3P7VHWTn9U5nSFG8uRrN5LC+zJka+k5UenvYDTUCcbE+yfM Lwd7W7eM1zuBKIjYDtTL =+QfU -----END PGP SIGNATURE-----

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 9:25 AM, Kevin Gessner <kgessner@etsy.com> wrote:
> Hello internals team! I'd like to propose an RFC to allow traits to > implement interfaces. >
I've created a proper RFC wiki page here with the draft: https://wiki.php.net/rfc/traits-with-interfaces It includes more detail and several example code snippets. Thanks all for your feedback so far.

Larry Garfield

10 years ago
On Wed, Feb 17, 2016, at 01:05 PM, Kevin Gessner wrote:
> On Wed, Feb 17, 2016 at 9:25 AM, Kevin Gessner <kgessner@etsy.com> wrote: > > > Hello internals team! I'd like to propose an RFC to allow traits to > > implement interfaces. > > > > I've created a proper RFC wiki page here with the draft: > https://wiki.php.net/rfc/traits-with-interfaces > > It includes more detail and several example code snippets. Thanks all > for > your feedback so far.
Thanks, Kevin. At the risk of a "me too" post, I would also be in favor of allowing traits to declare an interface but NOT in favor of a use-ing class implicitly implementing the interface as a result. I'd rather the class still need to self-declare the interface; that it uses a trait to fulfill that contract is irrelevant to the outside world. At that point, the benefit becomes two-fold: 1) Communicate to users of the trait what the intent of the trait is: To provide the boilerplate for some interface, or portion thereof. This could be covered by PHPDoc, potentially, and I've suggested the same for PSR-5 before. 2) Ask the engine to do a syntax check on the class to make sure I did not forget something or screwed up in some way. That is, not fulfilling the interface on the trait would cause a parse error (or an error in my IDE), forcing me to fix my bug before I even try running it. PHPDoc would NOT have this benefit. Thus I feel this addition does have value that a PHPDoc alternative would not. --Larry Garfield

Chase Peeler

10 years ago
On Thu, Feb 18, 2016 at 5:35 AM Larry Garfield <larry@garfieldtech.com> wrote:
> On Wed, Feb 17, 2016, at 01:05 PM, Kevin Gessner wrote: > > On Wed, Feb 17, 2016 at 9:25 AM, Kevin Gessner <kgessner@etsy.com> > wrote: > > > > > Hello internals team! I'd like to propose an RFC to allow traits to > > > implement interfaces. > > > > > > > I've created a proper RFC wiki page here with the draft: > > https://wiki.php.net/rfc/traits-with-interfaces > > > > It includes more detail and several example code snippets. Thanks all > > for > > your feedback so far. > > Thanks, Kevin. > > At the risk of a "me too" post, I would also be in favor of allowing > traits to declare an interface but NOT in favor of a use-ing class > implicitly implementing the interface as a result. I'd rather the class > still need to self-declare the interface; that it uses a trait to > fulfill that contract is irrelevant to the outside world. >
I agree, in theory, but I also think we introduce confusion. Every other OO rule says you get "everything*" from what you are using (extending a subclass gets you all the parent classes, implementing an interface that extends an interface gets you the parent interface, using a trait that uses a trait gets you both traits, etc). Perhaps we need a different keyword? trait foo providesfor myInterface { } I'm not sold on that keyword, it's just the first thing that came to mind. *I know you don't get private methods and properties in subclasses, but that's something dictated by the class itself. The class says "you can't have these if you extend me" instead of the engine saying "you aren't implementing that when you use the trait because it's a trait" At that point, the benefit becomes two-fold:
> > 1) Communicate to users of the trait what the intent of the trait is: To > provide the boilerplate for some interface, or portion thereof. This > could be covered by PHPDoc, potentially, and I've suggested the same for > PSR-5 before. > > Agreed
> 2) Ask the engine to do a syntax check on the class to make sure I did > not forget something or screwed up in some way. That is, not fulfilling > the interface on the trait would cause a parse error (or an error in my > IDE), forcing me to fix my bug before I even try running it. PHPDoc > would NOT have this benefit. > > Thus I feel this addition does have value that a PHPDoc alternative > would not. > > Good point. I was of the opinion "we can just use comments to declare
intent," but hadn't really thought of using the engine in this way. It makes sense.
> --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --
-- Chase chasepeeler@gmail.com

Kevin Gessner

10 years ago
Hi Chase and Larry, On Thu, Feb 18, 2016 at 10:11 AM, Chase Peeler <chasepeeler@gmail.com> wrote:
> On Thu, Feb 18, 2016 at 5:35 AM Larry Garfield <larry@garfieldtech.com> > wrote: > I'd rather the class > > still need to self-declare the interface; that it uses a trait to > > fulfill that contract is irrelevant to the outside world. > > > I agree, in theory, but I also think we introduce confusion. Every other OO > rule says you get "everything*" from what you are using (extending a > subclass gets you all the parent classes, implementing an interface that > extends an interface gets you the parent interface, using a trait that uses > a trait gets you both traits, etc). Perhaps we need a different keyword? >
I'm wary of a new keyword, particularly because this situation isn't really that different from what "implements" means. I think if we wanted to go down the route of new syntax, I'd want to expand the `use` statement for traits to include or exclude any interfaces that a trait provides. But I don't think a syntax change is worth it for this feature, for the cognitive and language overhead.
> [snip] > > At that point, the benefit becomes two-fold: > > > > 1) Communicate to users of the trait what the intent of the trait is: To > > provide the boilerplate for some interface, or portion thereof. This > > could be covered by PHPDoc, potentially, and I've suggested the same for > > PSR-5 before. > > > > Agreed
> > > 2) Ask the engine to do a syntax check on the class to make sure I did > > not forget something or screwed up in some way. That is, not fulfilling > > the interface on the trait would cause a parse error (or an error in my > > IDE), forcing me to fix my bug before I even try running it. PHPDoc > > would NOT have this benefit. > > > > Thus I feel this addition does have value that a PHPDoc alternative > > would not. > > > > Good point. I was of the opinion "we can just use comments to declare > intent," but hadn't really thought of using the engine in this way. It > makes sense. >
That makes sense -- I like having the language enforce intent, rather than just comments (which always rot, in my experience).

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 First off: I like the idea of a trait being able to implement an interface and having PHP validating that because I am like Larry and prefer language construct over comments. On 2/18/2016 4:11 PM, Chase Peeler wrote:
> > I agree, in theory, but I also think we introduce confusion. Every > other OO rule says you get "everything*" from what you are using > (extending a subclass gets you all the parent classes, implementing > an interface that extends an interface gets you the parent > interface, using a trait that uses a trait gets you both traits, > etc). [...] >
I disagree on this. Traits are already special and adding interface support to them that are not automatically inherited does not yield confusion from my point of view. Most people will have a hard time understanding them in the first place. Again, traits are "reusable dead code" that don't do anything on their own. Hence, if they suddenly start doing anything other it gets weird. The class has to decide and only the class and it must be opt-in and not opt-out because this becomes a conscious decision rather than: "Dafuq, why is object A going through the method with the type hint against interface B although it does not implement it? Oh, a trait... Well I guess I need to **unimplement** the interface then." This is crying out loud for unintended bugs in software. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWxguAAAoJEOKkKcqFPVVr17QP/2iYhhv3dndfURkA/RW0lSTr CTN6gkIxDSEoGOzxk0HXAL6D9jgXOqhfsavWFLd0bdS12+2tfbi4+tz1VSGhBGoD fIYyvOh6gx7nnI4p8v7PTegaWPb2xuKf6D7+ogpFHH0PSi27gTV1Rgh6m/kdnH7+ wMKKybl7k1e85LEGNTs0PpXYRKNBKMK6EuoiQjbT3gPMle3ZQZYEDttOVHSKUmoO heu0IHEkunzE7MtTMujqj6xgR2X3ce2SfXVsxLR3J0pBh7nFyfcm21tDz7a5gLE6 3qisBhjVBjAU9tAO1mbLJWzHGcyDLCBSncFJfpVgbi5I2OYyrsMCIy8fvMxAW6C3 zeP1kSVz2VHPQpG6vpwapSIPLB2bkGKxd8o2c9QM+RaF4c6gwNi6PWPYI2t/w8TH iAq0jvQFdpRBAGpUyRpYrQ2/0clYLkl2qC/PRtA7SB19/Vsq3rROWqN0uKFtTb4K iNHE7+9CQV8n/p0/DUm99wW42IGM+5bXGNcY7a+DWhC+eJsF7a8BohvUWay5sL4h KZZ4VT8Khp5vbP3gpz7z8ugNMuFcIHkvJnUYR4QD9J1Ml5VpGH1pCw1uqKCN0qFX pzmo84og2xVr+r1OJ5DjSbXQE6WDSMvuhmpayZWOfxPa7a80KqYkl9pczQRAHz6X DvoLGPdq7s2rpknUYS84 =9OHz -----END PGP SIGNATURE-----

Kevin Gessner

10 years ago
On Wed, Feb 17, 2016 at 2:05 PM, Kevin Gessner <kgessner@etsy.com> wrote:
> On Wed, Feb 17, 2016 at 9:25 AM, Kevin Gessner <kgessner@etsy.com> wrote: > >> Hello internals team! I'd like to propose an RFC to allow traits to >> implement interfaces. >> > > I've created a proper RFC wiki page here with the draft: > https://wiki.php.net/rfc/traits-with-interfaces > > It includes more detail and several example code snippets. Thanks all for > your feedback so far. >
I've just updated the RFC to v0.2, presenting two proposals to be voted separately: one to allow traits to declare and implement interfaces, and a second (dependent on the first) to propagate interface declarations from traits to classes. I'd appreciate any additional feedback on this new version! Cheers -- Kevin

Kevin Gessner

10 years ago
On Thu, Feb 18, 2016 at 4:13 PM, Kevin Gessner <kgessner@etsy.com> wrote:
> On Wed, Feb 17, 2016 at 2:05 PM, Kevin Gessner <kgessner@etsy.com> wrote: > >> I've created a proper RFC wiki page here with the draft: >> https://wiki.php.net/rfc/traits-with-interfaces >> >> It includes more detail and several example code snippets. Thanks all >> for your feedback so far. >> > > I've just updated the RFC to v0.2, presenting two proposals to be voted > separately: one to allow traits to declare and implement interfaces, and a > second (dependent on the first) to propagate interface declarations from > traits to classes. >
I've created a php-src pull request for Proposal 1 of the RFC, allowing traits to declare and implement interfaces: https://github.com/php/php-src/pull/1773 Reviews and feedback welcome! I haven't yet started on an implementation for Proposal 2. Cheers -- Kevin

Alex Bowers

10 years ago
Would a fair solution to this be having the using class define whether to inherit the implementations? Perhaps a new keyword akin to 'propagated', so the code will read Class Foo { Use propagated TraitName; } Only then will the implementations from that trait bubble through. If it isn't declared then the implementations are not visible. This should keep all backwards compatibility and keep code readable since now we can immediately tell which traits being used do we want the implementations for in the main class. On 22 Feb 2016 20:19, "Kevin Gessner" <kgessner@etsy.com> wrote:

Chris Riley

10 years ago
On 23 February 2016 at 00:37, Alex Bowers <bowersbros@gmail.com> wrote:
> Would a fair solution to this be having the using class define whether to > inherit the implementations? Perhaps a new keyword akin to 'propagated', so > the code will read > > Class Foo { > Use propagated TraitName; > } > > Only then will the implementations from that trait bubble through. If it > isn't declared then the implementations are not visible. This should keep > all backwards compatibility and keep code readable since now we can > immediately tell which traits being used do we want the implementations for > in the main class. > On 22 Feb 2016 20:19, "Kevin Gessner" <kgessner@etsy.com> wrote: > > > On Thu, Feb 18, 2016 at 4:13 PM, Kevin Gessner <kgessner@etsy.com> > wrote: > > > > > On Wed, Feb 17, 2016 at 2:05 PM, Kevin Gessner <kgessner@etsy.com> > > wrote: > > > > > >> I've created a proper RFC wiki page here with the draft: > > >> https://wiki.php.net/rfc/traits-with-interfaces > > >> > > >> It includes more detail and several example code snippets. Thanks all > > >> for your feedback so far. > > >> > > > > > > I've just updated the RFC to v0.2, presenting two proposals to be voted > > > separately: one to allow traits to declare and implement interfaces, > and > > a > > > second (dependent on the first) to propagate interface declarations > from > > > traits to classes. > > > > > > > I've created a php-src pull request for Proposal 1 of the RFC, allowing > > traits to declare and implement interfaces: > > https://github.com/php/php-src/pull/1773 > > > > Reviews and feedback welcome! > > > > I haven't yet started on an implementation for Proposal 2. > > > > Cheers > > -- Kevin > > >
This isn't such a great idea as it will cause some of traits functionality to be broken: I can currently use a trait and alias its methods and change their visibility. If a trait implements an interface which is copied onto my class I can no longer do this as the compiler will throw a fatal error for the class not correctly implementing the interface that got copied from the trait. If you decide to continue pursuing this RFC, I'd like to see some thought given to the above; perhaps it could be as simple as not copying the interface if aliases/visibility changes are made in the use or perhaps something more complex which allows specifying which interfaces should be copied into the class from the trait. ~C

Kevin Gessner

10 years ago
On Tue, Feb 23, 2016 at 4:48 AM, Chris Riley <t.carnage@gmail.com> wrote:
> This isn't such a great idea as it will cause some of traits functionality > to be broken: I can currently use a trait and alias its methods and change > their visibility. If a trait implements an interface which is copied onto > my class I can no longer do this as the compiler will throw a fatal error > for the class not correctly implementing the interface that got copied from > the trait. >
This is only true under Proposal 2 of the RFC, and this is listed as an open issue: https://wiki.php.net/rfc/traits-with-interfaces#proposal_2 Proposal 1, where the engine enforces interface declarations on traits, doesn't affect how traits are inserted and still allows aliasing.
> If you decide to continue pursuing this RFC, I'd like to see some thought > given to the above; perhaps it could be as simple as not copying the > interface if aliases/visibility changes are made in the use or perhaps > something more complex which allows specifying which interfaces should be > copied into the class from the trait. >
I agree that there are several reasonable solutions here, but I'm not sure which is the best. It does feel too aggressive to have it be a fatal error if aliasing undoes the interface implementation. At the same time, introducing new syntax to enable aliasing to work with interface propagation defeats the purpose, which is to make it easier and simpler to implement an interface via a trait. I'd be interested to hear if you have any ideas about how this should look. I'm mostly unfamiliar with aliasing traits in practice, so I'd be curious to see how this RFC would affect your code. Cheers -- Kevin

Chase Peeler

10 years ago
On Wed, Feb 24, 2016 at 4:46 PM Kevin Gessner <kgessner@etsy.com> wrote:
> On Tue, Feb 23, 2016 at 4:48 AM, Chris Riley <t.carnage@gmail.com> wrote: > > > This isn't such a great idea as it will cause some of traits > functionality > > to be broken: I can currently use a trait and alias its methods and > change > > their visibility. If a trait implements an interface which is copied onto > > my class I can no longer do this as the compiler will throw a fatal error > > for the class not correctly implementing the interface that got copied > from > > the trait. > > > > This is only true under Proposal 2 of the RFC, and this is listed as an > open issue: https://wiki.php.net/rfc/traits-with-interfaces#proposal_2 > > Proposal 1, where the engine enforces interface declarations on traits, > doesn't affect how traits are inserted and still allows aliasing. > > > > If you decide to continue pursuing this RFC, I'd like to see some thought > > given to the above; perhaps it could be as simple as not copying the > > interface if aliases/visibility changes are made in the use or perhaps > > something more complex which allows specifying which interfaces should be > > copied into the class from the trait. > > > > I agree that there are several reasonable solutions here, but I'm not sure > which is the best. It does feel too aggressive to have it be a fatal error > if aliasing undoes the interface implementation. At the same time, > introducing new syntax to enable aliasing to work with interface > propagation defeats the purpose, which is to make it easier and simpler to > implement an interface via a trait. > > I'd be interested to hear if you have any ideas about how this should > look. I'm mostly unfamiliar with aliasing traits in practice, so I'd be > curious to see how this RFC would affect your code. > > Since the class doesn't know you are using the trait to implement the
interface, if you alias the trait method, then the class no longer implements the interface unless it defines the method that is now missing itself. Remember, the contents of the trait are just getting copy/pasted into the class. If you alias a method, then the method gets pasted in using the alias, not the original name. That being said, it should be rare that you would alias a method and then not implement the method yourself - as the need to redefine/expand on its functionality is the reason you usually alias a trait method.
> Cheers > -- Kevin >
-- -- Chase chasepeeler@gmail.com

Chris Riley

10 years ago
On 24 February 2016 at 22:04, Chase Peeler <chasepeeler@gmail.com> wrote:
> > > On Wed, Feb 24, 2016 at 4:46 PM Kevin Gessner <kgessner@etsy.com> wrote: > >> On Tue, Feb 23, 2016 at 4:48 AM, Chris Riley <t.carnage@gmail.com> wrote: >> >> > This isn't such a great idea as it will cause some of traits >> functionality >> > to be broken: I can currently use a trait and alias its methods and >> change >> > their visibility. If a trait implements an interface which is copied >> onto >> > my class I can no longer do this as the compiler will throw a fatal >> error >> > for the class not correctly implementing the interface that got copied >> from >> > the trait. >> > >> >> This is only true under Proposal 2 of the RFC, and this is listed as an >> open issue: https://wiki.php.net/rfc/traits-with-interfaces#proposal_2 >> >> Proposal 1, where the engine enforces interface declarations on traits, >> doesn't affect how traits are inserted and still allows aliasing. >> >> >> > If you decide to continue pursuing this RFC, I'd like to see some >> thought >> > given to the above; perhaps it could be as simple as not copying the >> > interface if aliases/visibility changes are made in the use or perhaps >> > something more complex which allows specifying which interfaces should >> be >> > copied into the class from the trait. >> > >> >> I agree that there are several reasonable solutions here, but I'm not sure >> which is the best. It does feel too aggressive to have it be a fatal >> error >> if aliasing undoes the interface implementation. At the same time, >> introducing new syntax to enable aliasing to work with interface >> propagation defeats the purpose, which is to make it easier and simpler to >> implement an interface via a trait. >> >> I'd be interested to hear if you have any ideas about how this should >> look. I'm mostly unfamiliar with aliasing traits in practice, so I'd be >> curious to see how this RFC would affect your code. >> >> Since the class doesn't know you are using the trait to implement the > interface, if you alias the trait method, then the class no longer > implements the interface unless it defines the method that is now missing > itself. > > Remember, the contents of the trait are just getting copy/pasted into the > class. If you alias a method, then the method gets pasted in using the > alias, not the original name. > > That being said, it should be rare that you would alias a method and then > not implement the method yourself - as the need to redefine/expand on its > functionality is the reason you usually alias a trait method. > > > >> Cheers >> -- Kevin >> > -- > -- Chase > chasepeeler@gmail.com >
Aliasing isn't something I use often; however changing visibility is - such that the methods in the trait become an implementation detail for the class in question and don't pollute it's public API. That would break if an interface was copied onto my class.

Nikita Popov

10 years ago
On Wed, Feb 17, 2016 at 3:25 PM, Kevin Gessner <kgessner@etsy.com> wrote:
> Hello internals team! I'd like to propose an RFC to allow traits to > implement interfaces. > > I've noticed s pattern in Etsy's code and elsewhere, where a trait provides > a common implementation of an interface. Classes that use the trait are > required to also explicitly declare the interface to benefit. I propose > that traits be permitted to declare and implement interfaces. Classes that > use such a trait would then implement the interface, as though it were > declared on the class, without declaring the interface explicitly. > > I believe this small change would be a useful improvement to the OO > system. I've written a draft RFC, but I don't have karma to create the > wiki page for it. Could someone please grant wiki karma to my account, > kevingessner? > > I don't yet have an implementation, but I'll be starting on one once I've > posted the RFC. I look forward to your thoughts and feedback. >
Without given an opinion on the RFC itself, this might be interesting for context: http://hhvm.com/blog/9581/trait-and-interface-requirements-in-hack HHVM already supports "trait Foo implements Iface" with the semantics that the interface is also implemented by the using class. Additionally HHVM supports a notion of "require implements Iface" which means that the using class must implement this interface. Nikita

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 2/18/2016 7:28 PM, Nikita Popov wrote:
> > Without given an opinion on the RFC itself, this might be > interesting for context: > > http://hhvm.com/blog/9581/trait-and-interface-requirements-in-hack > > HHVM already supports "trait Foo implements Iface" with the > semantics that the interface is also implemented by the using > class. > > Additionally HHVM supports a notion of "require implements Iface" > which means that the using class must implement this interface. > > Nikita >
They also allow *extends* that I would find useful as well. I really like the *require extends/implements* approach because it is very explicit like *final*. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWxhAhAAoJEOKkKcqFPVVrdT0P/AuCP09l04QCp5DP0SDtJMxv 02zShunhZHGW7FNWSyNYQvoJ8HdtkuUD94L9nPry6T+J86S1sQjouTu4xuHoOMNj gsEKYuwygfax/v8h48DzonY5dhFebNJVHUTR/7pmcxJx2MyZzKpaoeQFFieBBelP pf0YltxlBsImDfFDXcWwDuc2pgup1AuVMvO1PTJ9jshyNdqwcukmAIWl6VvKPLYN 9uRXrAYh4CiToosgqWmfjkPYungnSd0Zlqy0V509indVQQDLo76PLgmXmmonbcDu GzIOtnAzSq3ibDUw5E2aVjknvPXTCf3z4v0EDqyiHPOfLhbgU9x8Sdr5Plwj+OcN 64Xu+u2taZYqvcGQTszsUvlnLp6+YPz7HS0q+qqC5v5rwWDaqALoB8APSyEsHZ0S CKHq0ES/D4dwEYpTjCpGz/ZkyydhyZQ4UIn7g4VHPiYzA/QnHQ1C5H1vGagUOQ1u 7s/9TXrNg1fvHDVFSRS6v5zU8iuXGrZJIsO7N/VHLSyCe9IVF1++/bAhaqjgcaZM Tx7SzMZ1FVKbSbEGJstIq2onvkMMDC3hWdJItbS+ug9tG6wazhHBU/MJPpacawDL 5Xup3nhT4bB5jUJ6mBAnLSIFsG18Mc+NBrl8FwEEvx0nvXGTVJs4BNgW2f3EUpfr axg0SDiBD/NW8jYscosj =uXXN -----END PGP SIGNATURE-----

Chase Peeler

10 years ago
On Thu, Feb 18, 2016 at 1:29 PM Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Wed, Feb 17, 2016 at 3:25 PM, Kevin Gessner <kgessner@etsy.com> wrote: > > > Hello internals team! I'd like to propose an RFC to allow traits to > > implement interfaces. > > > > I've noticed s pattern in Etsy's code and elsewhere, where a trait > provides > > a common implementation of an interface. Classes that use the trait are > > required to also explicitly declare the interface to benefit. I propose > > that traits be permitted to declare and implement interfaces. Classes > that > > use such a trait would then implement the interface, as though it were > > declared on the class, without declaring the interface explicitly. > > > > I believe this small change would be a useful improvement to the OO > > system. I've written a draft RFC, but I don't have karma to create the > > wiki page for it. Could someone please grant wiki karma to my account, > > kevingessner? > > > > I don't yet have an implementation, but I'll be starting on one once I've > > posted the RFC. I look forward to your thoughts and feedback. > > > > Without given an opinion on the RFC itself, this might be interesting for > context: > > http://hhvm.com/blog/9581/trait-and-interface-requirements-in-hack > > HHVM already supports "trait Foo implements Iface" with the semantics that > the interface is also implemented by the using class. > > Additionally HHVM supports a notion of "require implements Iface" which > means that the using class must implement this interface. > > Why should it matter if the class using the trait actually implements the
interface? If the person just wants the functionality, but, for whatever reason, doesn't want to actually implement the interface, why should it matter? I think the trait should HELP the person implementing an interface. The trait should be able to say (whether via comments or an engine enforceable construct) "Hey, I've done everything needed for you to implement this interface" but, at no time, should the trait force the class utilizing it to interact (via extend or implement or use) a 3rd artifact. trait foo implements myinterface {} class bar { use foo;} class baz implements myinterface { use foo;} Both of the above should be legal, and $myBar instanceof myinterface should return false; Traits also already have the ability to force the using class to define certain functionality by declaring methods abstract - however, the trait doesn't care if it is implementing that method via inheritance, because some other interface requires it be implement, or via another trait.
> Nikita >
-- -- Chase chasepeeler@gmail.com

Kevin Gessner

10 years ago
On Thu, Feb 18, 2016 at 2:16 PM, Chase Peeler <chasepeeler@gmail.com> wrote:
> > > On Thu, Feb 18, 2016 at 1:29 PM Nikita Popov <nikita.ppv@gmail.com> wrote: > >> HHVM already supports "trait Foo implements Iface" with the semantics that >> the interface is also implemented by the using class. >> >> Additionally HHVM supports a notion of "require implements Iface" which >> means that the using class must implement this interface. >> >
This was mentioned in the original traits RFC, but never got voted or implemented: https://wiki.php.net/rfc/horizontalreuse#requiring_composing_class_to_implement_interface It's orthogonal to this RFC.
> >> Why should it matter if the class using the trait actually implements the > interface? If the person just wants the functionality, but, for whatever > reason, doesn't want to actually implement the interface, why should it > matter? > > I think the trait should HELP the person implementing an interface. The > trait should be able to say (whether via comments or an engine enforceable > construct) "Hey, I've done everything needed for you to implement this > interface" but, at no time, should the trait force the class utilizing it > to interact (via extend or implement or use) a 3rd artifact. > > trait foo implements myinterface {} > class bar { use foo;} > class baz implements myinterface { use foo;} > > Both of the above should be legal, and $myBar instanceof myinterface > should return false; >
This is Proposal 1 in the new draft of my RFC: https://wiki.php.net/rfc/traits-with-interfaces Does that proposal sound like what you'd want? Cheers -- Kevin

Chase Peeler

10 years ago
On Fri, Feb 19, 2016 at 2:13 PM Kevin Gessner <kgessner@etsy.com> wrote:
> On Thu, Feb 18, 2016 at 2:16 PM, Chase Peeler <chasepeeler@gmail.com> > wrote: > >> On Thu, Feb 18, 2016 at 1:29 PM Nikita Popov <nikita.ppv@gmail.com> >> wrote: >> > HHVM already supports "trait Foo implements Iface" with the semantics that >>> the interface is also implemented by the using class. >>> >>> Additionally HHVM supports a notion of "require implements Iface" which >>> means that the using class must implement this interface. >>> >> > This was mentioned in the original traits RFC, but never got voted or > implemented: > https://wiki.php.net/rfc/horizontalreuse#requiring_composing_class_to_implement_interface > > It's orthogonal to this RFC. > > >> >>> Why should it matter if the class using the trait actually implements >> the interface? If the person just wants the functionality, but, for >> whatever reason, doesn't want to actually implement the interface, why >> should it matter? >> >> I think the trait should HELP the person implementing an interface. The >> trait should be able to say (whether via comments or an engine enforceable >> construct) "Hey, I've done everything needed for you to implement this >> interface" but, at no time, should the trait force the class utilizing it >> to interact (via extend or implement or use) a 3rd artifact. >> >> trait foo implements myinterface {} >> class bar { use foo;} >> class baz implements myinterface { use foo;} >> >> Both of the above should be legal, and $myBar instanceof myinterface >> should return false; >> > > This is Proposal 1 in the new draft of my RFC: > https://wiki.php.net/rfc/traits-with-interfaces > > Does that proposal sound like what you'd want? > > I don't really have any objections to proposal 1. Part of me would still
like to see a different keyword used, but I don't think that's a huge deal. I think the important part is not propagating the implementation (proposal 2). My comments above, however, were more in relation to the HHVM notion of "requires implement interface" which I don't think either proposal does.
> Cheers > -- Kevin > >> --
-- Chase chasepeeler@gmail.com

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 2/19/2016 8:34 PM, Chase Peeler wrote:
> My comments above, however, were more in relation to the HHVM > notion of "requires implement interface" which I don't think either > proposal does. >
I cannot vote but I would like to repeat that the HHVM approach solves all desires. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWx3ANAAoJEOKkKcqFPVVrnUoP/jEjDHkxDuGVagUvkIqaGXbL NQqrgXvSuv5kjsLXNMsQcYL7NVdfRK18PpZ8V0rVi6bzph4azOWC/Y01NyuW+CAN T+eccL5yiI7UPCh/Iiif4d9nGu6gCBEXgvu4ADtK5Z8lQx+P+UhXahCksTeNXPl5 5x3kNBEHKI4HrnKeDZpRyLg+waJxkIzn7Ulrd4OPta8zJvhOF2r0SSwugbE703nt d9n9XKrp8xdji3uoNeLD95FLEjWi85U0ANO2iFKgawGAHnKRXeLOaTL8Jzygjmsy YegO7OQzKR+SqCTivTSejsTKcMJQKJi3Uj1zHu4j+P8hIuGvHBA/LcfP030jj/vH 3L4XZnv0/gogX4EwJ4K3BjzKyWKwMxtzRoQttClGbVn/3qO7y5cpUe6W9WhetMh5 Z+VnuuyEGLE0wvjVnXOHA7ZC/mykNFUIgEv6nvoCIdLWXtST16ozP/FYK9o1hQyl jSlC66JPIqD2kvJxAR7lEJfIv0pz8qfptt45zMitapd0cr2r63FJW7usg1T7VIpI oUKKGIKX8PUnYAYqPfsK6zIENqZwQQ6FkmWVYKF2gSE5zcOpt4P2J23AMnGZe70/ wpJoo37MKmnOrVk99FisHFpIOCqQ3YS8kocDC390g1LsGDv++aapfAWUo073JpuR ySxOW97em8OevmKFtMcb =nzH/ -----END PGP SIGNATURE-----

Chase Peeler

10 years ago
On Fri, Feb 19, 2016 at 2:42 PM Fleshgrinder <php@fleshgrinder.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA256 > > On 2/19/2016 8:34 PM, Chase Peeler wrote: > > My comments above, however, were more in relation to the HHVM > > notion of "requires implement interface" which I don't think either > > proposal does. > > > > I cannot vote but I would like to repeat that the HHVM approach solves > all desires. > > I can't vote either :-)
I don't agree. The HHVM approach of allowing a trait to indicate the a utilizing class MUST implement an interface is, in my opinion, not a good thing. It allows the trait to force a utilizing class to interact with a third entity, which I don't think a trait should be able to do. The trait can say "hey, if you want to use me, you have to implement my abstract methods" but the trait shouldn't be able to force you to include his friends. Again, that part of the HHVM approach isn't being proposed, so it's really a moo point anyway.
> - -- > Richard "Fleshgrinder" Fussenegger > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v2 > > iQIcBAEBCAAGBQJWx3ANAAoJEOKkKcqFPVVrnUoP/jEjDHkxDuGVagUvkIqaGXbL > NQqrgXvSuv5kjsLXNMsQcYL7NVdfRK18PpZ8V0rVi6bzph4azOWC/Y01NyuW+CAN > T+eccL5yiI7UPCh/Iiif4d9nGu6gCBEXgvu4ADtK5Z8lQx+P+UhXahCksTeNXPl5 > 5x3kNBEHKI4HrnKeDZpRyLg+waJxkIzn7Ulrd4OPta8zJvhOF2r0SSwugbE703nt > d9n9XKrp8xdji3uoNeLD95FLEjWi85U0ANO2iFKgawGAHnKRXeLOaTL8Jzygjmsy > YegO7OQzKR+SqCTivTSejsTKcMJQKJi3Uj1zHu4j+P8hIuGvHBA/LcfP030jj/vH > 3L4XZnv0/gogX4EwJ4K3BjzKyWKwMxtzRoQttClGbVn/3qO7y5cpUe6W9WhetMh5 > Z+VnuuyEGLE0wvjVnXOHA7ZC/mykNFUIgEv6nvoCIdLWXtST16ozP/FYK9o1hQyl > jSlC66JPIqD2kvJxAR7lEJfIv0pz8qfptt45zMitapd0cr2r63FJW7usg1T7VIpI > oUKKGIKX8PUnYAYqPfsK6zIENqZwQQ6FkmWVYKF2gSE5zcOpt4P2J23AMnGZe70/ > wpJoo37MKmnOrVk99FisHFpIOCqQ3YS8kocDC390g1LsGDv++aapfAWUo073JpuR > ySxOW97em8OevmKFtMcb > =nzH/ > -----END PGP SIGNATURE----- > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --
-- Chase chasepeeler@gmail.com

Fleshgrinder

10 years ago
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 2/19/2016 8:47 PM, Chase Peeler wrote:
> I don't agree. The HHVM approach of allowing a trait to indicate > the a utilizing class MUST implement an interface is, in my > opinion, not a good thing. It allows the trait to force a utilizing > class to interact with a third entity, which I don't think a trait > should be able to do. The trait can say "hey, if you want to use > me, you have to implement my abstract methods" but the trait > shouldn't be able to force you to include his friends. > > Again, that part of the HHVM approach isn't being proposed, so it's > really a moo point anyway. >
My argumentation from the very beginning, however, I understood that some people would love to see exactly this possibility: to force using classes. I am all for compromises to make everyone happy. However, if we agree that we do not want to see the "require" then I am totally happy to see support for "implements" in they way that it is not inherited by the using class at all for the reasons I explained earlier. - -- Richard "Fleshgrinder" Fussenegger -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJWx3KEAAoJEOKkKcqFPVVrZcoQAMTX8EiwtvtYlL8SJc030qUP t8u/L6TlJt/euaWFCnvuWcqe0gg674/cqC7uPoSz1y/Zql2YD52Hj9VDBMlg3FWT 5D/uS3KUgjEFUGnn45fUpEZSl08qWNKMqYQcr3HmK3oMTHISPxfLv9xw3PdLtnIt iQBlSM/PVFsC5nxdbClxMnEBr3TSVk7nehSCkneEs1Ew8hCEs/Q/dArj0nwjDJYX AoO0qsbTV9PUt0OiHMgGQ8MtubKVTdPd8WgsUT1wN3bXtxBHAQOHsMav8ZY5PbDM Atq1dMnmKP5wNmpMXNc/01TYJHws2MxWeTdFF9pedd6u6jz1D7eNuea7uX9OQG5U r9KRBjeeIsthY+ertt1jVzJ43ufaclal4bvwbiS2/KkKfPOprGu42DrZDNtJZNdJ iOuWiZKAbhg9YA/NcoVx8YxwH2rbpdSJjyc84W5MCYjAofM4eEaYtj79gnZbd1Hj d/wqnKk4H6tRt1HjQLfoqBlFn27R6xNMe+1j5gRftNZkK0ZmHIMVt/I+aId/iFgK ultn1JFsk8E75Tj6XzD5r3KPa38B4WTQYp919Jrf6dY66RIZ3L9yRViBmoqxUml2 WOhZYLPQTUBLtxCAXLQYcp1UntbAEa9f89oLkhH6iekmCxcplfG7ka2fVoc+XqLk xgub9X1NTtsK7HYty6nf =2oRB -----END PGP SIGNATURE-----

Stephen Coakley

10 years ago
On Wed, 17 Feb 2016 09:25:50 -0500, Kevin Gessner wrote:
> Hello internals team! I'd like to propose an RFC to allow traits to > implement interfaces. > > I've noticed s pattern in Etsy's code and elsewhere, where a trait > provides a common implementation of an interface. Classes that use the > trait are required to also explicitly declare the interface to benefit. > I propose that traits be permitted to declare and implement interfaces. > Classes that use such a trait would then implement the interface, as > though it were declared on the class, without declaring the interface > explicitly. > > I believe this small change would be a useful improvement to the OO > system. I've written a draft RFC, but I don't have karma to create the > wiki page for it. Could someone please grant wiki karma to my account, > kevingessner? > > I don't yet have an implementation, but I'll be starting on one once > I've posted the RFC. I look forward to your thoughts and feedback. > > Thanks in advance -- Kevin > > Kevin Gessner Staff Software Engineer etsy.com
tl;dr: +1 and I really think that this language addition is useful and makes sense. Wow, I really want this feature. Reminds me of how powerful traits are in some other languages, such as Rust. It is very common to use traits to provide a common interface for something, but with some default implementations. Logging is a great example. Your interface might look like this (a familiar one, eh?): interface Logger { public function log($level, $message, array $context = array()); public function error($message, array $context = array()); public function warning($message, array $context = array()); // etc... } where the idea is that the `error()` and such methods are a convenience for calling `log()` with a specific logging level. Obviously, these methods will be implemented in the same fashion most of the time; a trait would be great: trait LoggerTrait implements Logger { abstract public function log($level, $message, array $context = array ()); public function error($message, array $context = array()) { return $this->log(ERROR, $message, $context); } // etc... } With this approach, I totally agree that allowing the `LoggerTrait` to implement the interface makes sense; it allows implementation to be enforced at the trait level. The second proposal that infers the interface implementation when using the trait is nice too (though not completely mandatory). It is pretty much the same situation where you do not have to re-implement an interface when extending a base class that already implements it.
-- Stephen

Davey

10 years ago
On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley <me@stephencoakley.com> wrote:
> On Wed, 17 Feb 2016 09:25:50 -0500, Kevin Gessner wrote: > > > Hello internals team! I'd like to propose an RFC to allow traits to > > implement interfaces. > > > > I've noticed s pattern in Etsy's code and elsewhere, where a trait > > provides a common implementation of an interface. Classes that use the > > trait are required to also explicitly declare the interface to benefit. > > I propose that traits be permitted to declare and implement interfaces. > > Classes that use such a trait would then implement the interface, as > > though it were declared on the class, without declaring the interface > > explicitly. > > > > I believe this small change would be a useful improvement to the OO > > system. I've written a draft RFC, but I don't have karma to create the > > wiki page for it. Could someone please grant wiki karma to my account, > > kevingessner? > > > > I don't yet have an implementation, but I'll be starting on one once > > I've posted the RFC. I look forward to your thoughts and feedback. > > > > Thanks in advance -- Kevin > > > > Kevin Gessner Staff Software Engineer etsy.com > > tl;dr: +1 and I really think that this language addition is useful and > makes sense. > > Wow, I really want this feature. Reminds me of how powerful traits are in > some other languages, such as Rust. It is very common to use traits to > provide a common interface for something, but with some default > implementations. > > Logging is a great example. Your interface might look like this (a > familiar one, eh?): > > interface Logger > { > public function log($level, $message, array $context = array()); > public function error($message, array $context = array()); > public function warning($message, array $context = array()); > // etc... > } > > where the idea is that the `error()` and such methods are a convenience > for calling `log()` with a specific logging level. Obviously, these > methods will be implemented in the same fashion most of the time; a trait > would be great: > > trait LoggerTrait implements Logger > { > abstract public function log($level, $message, array $context = array > ()); > public function error($message, array $context = array()) { > return $this->log(ERROR, $message, $context); > } > // etc... > } > > With this approach, I totally agree that allowing the `LoggerTrait` to > implement the interface makes sense; it allows implementation to be > enforced at the trait level. The second proposal that infers the interface > implementation when using the trait is nice too (though not completely > mandatory). It is pretty much the same situation where you do not have to > re-implement an interface when extending a base class that already > implements it. > > -- > Stephen
The aliases issue is a little more nuanced and potentially confusing, regardless of this interface thing: As you can see here: https://3v4l.org/L23LJ 1. If you simply alias (use foo { bar as bat; }) then you end up with an *additional* method with the new name, the trait method as defined is still brought in, and _will_ override inherited methods of the same name. 2. if you try to just change the visibility, you get a fatal error (Fatal error: Trait method bar has not been applied, because there are collisions with other trait methods), you must create an aliased name with the new visibility 3. Doing this (visibility + name) _only_ gives you the new method, which is _different_ behavior to #1 If the third behaved the same as the first, then this would be a non-issue. Unfortunately, changing this behavior would be a — particularly hard to diagnose — BC break and therefore cannot happen IMO. Perhaps we could look at an alternative such as: - traits can implement interfaces, which would ensure the _trait_ adheres to it, but _not_ automatically classes use'ing it. - Add a new syntax: "use Trait with Interface" or "use Trait implements Interface" or "use Trait { implements Interface; }" which *explicitly* calls out this usage Just some off-the-top of my head thinking as an alternative. - Davey

Rowan Collins

10 years ago
Davey Shafik wrote on 04/03/2016 07:17:
> 1. If you simply alias (use foo { bar as bat; }) then you end up with an > *additional* method with the new name, the trait method as defined is still > brought in, and_will_ override inherited methods of the same name.
Here's a clearer example of this: https://3v4l.org/RKHPt Unfortunately, you can't even use "insteadof" to directly bring the parent method back in [https://3v4l.org/qOS5T], but you can stub it out with a direct call to parent:: [https://3v4l.org/s9i4N].
> 3. Doing this (visibility + name)_only_ gives you the new method, which is > _different_ behavior to #1
I can't reproduce this: if I say "bar as private bat", the trait's bar still shows up, and is public, just as in the previous example: https://3v4l.org/1jH6o Your examples are rather confusing because they are effectively applying the same trait twice, at different levels of the hierarchy; I'm not sure this is a particularly likely scenario, or relevant to how interfaces should behave. Regards,
-- Rowan Collins [IMSoP]

Davey

10 years ago
On Fri, Mar 4, 2016 at 2:06 AM, Rowan Collins <rowan.collins@gmail.com> wrote:
> Davey Shafik wrote on 04/03/2016 07:17: > >> 1. If you simply alias (use foo { bar as bat; }) then you end up with an >> *additional* method with the new name, the trait method as defined is >> still >> brought in, and_will_ override inherited methods of the same name. >> > > Here's a clearer example of this: https://3v4l.org/RKHPt > > Unfortunately, you can't even use "insteadof" to directly bring the parent > method back in [https://3v4l.org/qOS5T], but you can stub it out with a > direct call to parent:: [https://3v4l.org/s9i4N]. > > 3. Doing this (visibility + name)_only_ gives you the new method, which is >> _different_ behavior to #1 >> > > I can't reproduce this: if I say "bar as private bat", the trait's bar > still shows up, and is public, just as in the previous example: > https://3v4l.org/1jH6o > > Your examples are rather confusing because they are effectively applying > the same trait twice, at different levels of the hierarchy; I'm not sure > this is a particularly likely scenario, or relevant to how interfaces > should behave. > > Regards,
Rowan, You are mid-reading, none of the classes in my examples extend the others, they are all just using the same trait in different ways. - Class a: use the trait with no aliases. Result: as expected - Class b: use the trait with a simple alias, no visibility change. Result: both methods - Class c: use the trait with and alias both name, and change visibility. Result: ONLY the aliased method - Class d: use the trait and "alias" to the same name, ONLY changing visibility. Result: causes a Fatal error, clashing with itself o.O For the one you can't re-produce, it's class 'c', which is stand-alone here: https://3v4l.org/K9o6Y - Davey

Jesse Schalken

10 years ago
Your class 'c' example (last link) only shows method 'bar' (the trait method) and not 'bat' (the aliased metod). The class has both, but 'bat' is hidden from get_class_methods() because it is private. On 6 Mar 2016 10:16 am, "Davey Shafik" <davey@php.net> wrote:

Rowan Collins

10 years ago
On 05/03/2016 23:15, Davey Shafik wrote:
> You are mid-reading, none of the classes in my examples extend the > others, they are all just using the same trait in different ways. > > - Class a: use the trait with no aliases. Result: as expected > - Class b: use the trait with a simple alias, no visibility change. > Result: both methods
Right, the reason I was looking at inheritance was this line:
> ... and_will_ override inherited methods of the same name.
I misunderstood that the rest of the examples were also about how inheritance behaved.
> For the one you can't re-produce, it's class 'c', which is stand-alone > here: https://3v4l.org/K9o6Y
Right, so this is actually consistent, because the resulting "stitched together" class looks like this: class C { public function bar() {} private function bat() {} } Whereas when the alias wasn't private, it looked liked this: class B { public function bar() {} public function bat() {} } You can actually change its visibility on one line and alias it on another: class e { use foo { bar as private; bar as bat; } } https://3v4l.org/80gKF Stitched togorther, that gives you: class e { private function bar() {} public function bat() {} } So the main confusion is that "bar as private bat" != "bar as private" + "bar as bat". :) Regards,
-- Rowan Collins [IMSoP]

Stephen Coakley

10 years ago
On 03/04/2016 01:17 AM, Davey Shafik wrote:
> On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley <me@stephencoakley.com> > wrote: > >> On Wed, 17 Feb 2016 09:25:50 -0500, Kevin Gessner wrote: >> >>> Hello internals team! I'd like to propose an RFC to allow traits to >>> implement interfaces. >>> >>> I've noticed s pattern in Etsy's code and elsewhere, where a trait >>> provides a common implementation of an interface. Classes that use the >>> trait are required to also explicitly declare the interface to benefit. >>> I propose that traits be permitted to declare and implement interfaces. >>> Classes that use such a trait would then implement the interface, as >>> though it were declared on the class, without declaring the interface >>> explicitly. >>> >>> I believe this small change would be a useful improvement to the OO >>> system. I've written a draft RFC, but I don't have karma to create the >>> wiki page for it. Could someone please grant wiki karma to my account, >>> kevingessner? >>> >>> I don't yet have an implementation, but I'll be starting on one once >>> I've posted the RFC. I look forward to your thoughts and feedback. >>> >>> Thanks in advance -- Kevin >>> >>> Kevin Gessner Staff Software Engineer etsy.com >> >> tl;dr: +1 and I really think that this language addition is useful and >> makes sense. >> >> Wow, I really want this feature. Reminds me of how powerful traits are in >> some other languages, such as Rust. It is very common to use traits to >> provide a common interface for something, but with some default >> implementations. >> >> Logging is a great example. Your interface might look like this (a >> familiar one, eh?): >> >> interface Logger >> { >> public function log($level, $message, array $context = array()); >> public function error($message, array $context = array()); >> public function warning($message, array $context = array()); >> // etc... >> } >> >> where the idea is that the `error()` and such methods are a convenience >> for calling `log()` with a specific logging level. Obviously, these >> methods will be implemented in the same fashion most of the time; a trait >> would be great: >> >> trait LoggerTrait implements Logger >> { >> abstract public function log($level, $message, array $context = array >> ()); >> public function error($message, array $context = array()) { >> return $this->log(ERROR, $message, $context); >> } >> // etc... >> } >> >> With this approach, I totally agree that allowing the `LoggerTrait` to >> implement the interface makes sense; it allows implementation to be >> enforced at the trait level. The second proposal that infers the interface >> implementation when using the trait is nice too (though not completely >> mandatory). It is pretty much the same situation where you do not have to >> re-implement an interface when extending a base class that already >> implements it. >> >> -- >> Stephen > > > The aliases issue is a little more nuanced and potentially confusing, > regardless of this interface thing: > > As you can see here: https://3v4l.org/L23LJ > > 1. If you simply alias (use foo { bar as bat; }) then you end up with an > *additional* method with the new name, the trait method as defined is still > brought in, and _will_ override inherited methods of the same name. > > 2. if you try to just change the visibility, you get a fatal error (Fatal > error: Trait method bar has not been applied, because there are collisions > with other trait methods), you must create an aliased name with the new > visibility > > 3. Doing this (visibility + name) _only_ gives you the new method, which is > _different_ behavior to #1 > > If the third behaved the same as the first, then this would be a non-issue. > Unfortunately, changing this behavior would be a — particularly hard to > diagnose — BC break and therefore cannot happen IMO. > > Perhaps we could look at an alternative such as: > > - traits can implement interfaces, which would ensure the _trait_ adheres > to it, but _not_ automatically classes use'ing it. > - Add a new syntax: "use Trait with Interface" or "use Trait implements > Interface" or "use Trait { implements Interface; }" which *explicitly* > calls out this usage > > Just some off-the-top of my head thinking as an alternative. > > - Davey >
I freely admit; the method aliasing does pose an interesting issue, and may be good enough reason to simply allow enforcing interfaces at the trait level and simply force users of the trait to explicitly implement the interface themselves. That way it is easy to check compliance -- aliasing a method part of the interface on the trait to a different name simply breaks the contract.
-- Stephen