On 03/04/2016 01:17 AM, Davey Shafik wrote:
On Thu, Mar 3, 2016 at 2:19 PM, Stephen Coakley <m...@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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to