Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:91515 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 35629 invoked from network); 6 Mar 2016 05:41:15 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 6 Mar 2016 05:41:15 -0000 X-Host-Fingerprint: 68.118.157.39 68-118-157-39.dhcp.mdsn.wi.charter.com Received: from [68.118.157.39] ([68.118.157.39:4136] helo=localhost.localdomain) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 15/70-29316-9F2CBD65 for ; Sun, 06 Mar 2016 00:41:14 -0500 Message-ID: <15.70.29316.9F2CBD65@pb1.pair.com> To: internals@lists.php.net References: <94.96.08749.188B8D65@pb1.pair.com> Date: Sat, 5 Mar 2016 23:41:10 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.5.1 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit X-Posted-By: 68.118.157.39 Subject: Re: [PHP-DEV] Re: [RFC] Traits with interfaces From: me@stephencoakley.com (Stephen Coakley) On 03/04/2016 01:17 AM, Davey Shafik wrote: > On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley > 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