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
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.
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--
--
-- Chase
chasepeeler@gmail.com
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
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:
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.
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:[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.
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:[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.
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:
- 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. - 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
Hello,
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
Hi Kevin,
Hi all,
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)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
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
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.
Richard "Fleshgrinder" Fussenegger
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2iQIcBAEBCAAGBQJWxLkuAAoJEOKkKcqFPVVrJe4P/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
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
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).
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.
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
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.
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-interfacesIt 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:
-
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. -
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
On Thu, Feb 18, 2016 at 5:35 AM Larry Garfield larry@garfieldtech.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-interfacesIt 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:
- 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
- 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
--
--
-- Chase
chasepeeler@gmail.com
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 classstill 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:
- 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
- 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).
--Larry Garfield
--
--
-- Chase
chasepeeler@gmail.com
-----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.
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
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-interfacesIt 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
I've created a proper RFC wiki page here with the draft:
https://wiki.php.net/rfc/traits-with-interfacesIt 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
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 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-interfacesIt 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/1773Reviews and feedback welcome!
I haven't yet started on an implementation for Proposal 2.
Cheers
-- Kevin
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 readClass 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 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-interfacesIt 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/1773Reviews 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
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
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_2Proposal 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
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_2Proposal 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.
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
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
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
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
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
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_interfaceIt'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-interfacesDoes 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
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
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
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256My 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 v2iQIcBAEBCAAGBQJWx3ANAAoJEOKkKcqFPVVrnUoP/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
chasepeeler@gmail.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
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
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
On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley me@stephencoakley.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.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 callinglog()
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
-
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. -
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 -
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
Davey Shafik wrote on 04/03/2016 07:17:
- 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].
- 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]
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:
- 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].
- 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/1jH6oYour 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
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 Fri, Mar 4, 2016 at 2:06 AM, Rowan Collins rowan.collins@gmail.com
wrote:Davey Shafik wrote on 04/03/2016 07:17:
- 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].
- Doing this (visibility + name)only gives you the new method, which
isdifferent 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/1jH6oYour 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.OFor the one you can't re-produce, it's class 'c', which is stand-alone
here: https://3v4l.org/K9o6Y
- Davey
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]
On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley me@stephencoakley.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.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 callinglog()
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.--
StephenThe aliases issue is a little more nuanced and potentially confusing,
regardless of this interface thing:As you can see here: https://3v4l.org/L23LJ
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.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
visibilityDoing this (visibility + name) only gives you the new method, which is
different behavior to #1If 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 usageJust 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