Re: Re: [RFC] Traits with interfaces
| From: | Stephen Coakley | Date: | Sun, 06 Mar 2016 05:41:10 +0000 |
| Subject: | Re: Re: [RFC] Traits with interfaces | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-91515@lists.php.net to get a copy of this message | ||
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: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. -- StephenOn Wed, 17 Feb 2016 09:25:50 -0500, Kevin Gessner wrote: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. - DaveyHello 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.comtl;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 theerror()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 theLoggerTraitto 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