Re: [Discussion] Implementing interfaces via traits

From: Date: Fri, 30 Aug 2024 14:15:06 +0000
Subject: Re: [Discussion] Implementing interfaces via traits
References: 1 2  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
On Fri, Aug 30, 2024, at 1:09 AM, Michał Marcin Brzuchalski wrote:
> Hi Brent,
>
> wt., 27 sie 2024 o 09:28 Brent Roose <[email protected]> napisał(a):
>> Good morning internals
>> 
>> 
>> I’d like to test the waters about an RFC idea: allowing traits to implement interfaces,
>> and consequently a class that uses such a trait will automatically implement the interface as well.
>> 
>> 
>> The original idea comes from Rust, where traits can be used as types. I read a very
>> inspiring post suggested by Larry, on the topic of “classic inheritance” vs the way Rust and Go
>> approach it [1]. The tl;dr is that both Rust and Go solve several pitfalls of classical inheritance
>> (the diamond problem and inheritance abuse), thanks to a much simpler approach. In Rust’s case
>> that is by using traits. If you have the time, I highly recommend reading that post, it’s super
>> interesting and it gives a lot of good arguments for rethinking inheritance.
>> 
>> 
>> Back to PHP, using traits as types seems impossible, since traits are a compile-time
>> copy/paste mechanism, which means there’s no type information available about them at runtime.
>> 
>> 
>> However, allowing traits to implement interfaces would solve this problem: these interfaces
>> would be copied over to classes during compile-time, and the interface’s type information is
>> available at runtime.. On top of that, traits already have well-defined rules for conflict
>> resolution, so we wouldn’t need any additional syntax to handle edge cases.
>> 
>> 
>> Even though PHP traits differ from Rust, PHP developers already seem to like the idea of
>> being able to “attach a type to a trait” one way or another. Let me name a couple of things that
>> happen today:
>> 
>> 
>>  • Laravel often provides “default implementations” for their interfaces via a trait
>> [2]. As mentioned before, traits already deal with conflict-resolution, so method collisions
>> aren’t a blocker.
>> 
>>  • Both PHPStan and Psalm have an annotation that forces trait users to implement an
>> interface [3], which is essentially the feature I’m describing, albeit via docblock annotations
>> instead of proper syntax.
>> 
>>  • Even though it was not accepted, the interface default methods RFC approached the
>> problem from a different angle [4]. While a majority disagreed that interfaces should implement
>> their own methods directly, I remember it was a heavily debated topic, and believe that approaching
>> it from the other side might be easier to accept.
> 
> With the recent RFC proposal for Default Expressions 
> <https://wiki.php.net/rfc/default_expression>
> [5], I believe it 
> presents an excellent opportunity to revisit the Interface Default 
> Methods proposal. The Default Expressions RFC addresses similar 
> functionality and, when combined with an opt-in feature flag, could 
> resolve many concerns raised during the previous discussion.

I... have no idea why you're conflating the Interface Default Methods RFC and the Default
Expressions RFC.  They have nothing to do with each other beyond having the word "default"
in their name.

I would be very much in favor of revisiting Interface Default Methods, though, as I think it would
be a strong feature, and it's one found in nearly all of our sibling languages at this point. 
PHP is weird for not having them.  I believe that would also be a better approach than traits that
link to interfaces, which achieves not-quite the same result with more steps.

> *Opt-In Feature Flag:* To address backward compatibility concerns, I 
> propose the introduction of a feature flag, such as 
> declare(default_methods = 1);, that could be applied when 
> implementing an interface or when an interface extends another. This 
> approach would allow developers to opt-in explicitly, preventing 
> unintended BC breaks and ensuring that the feature is adopted carefully 
> and intentionally.

Dear god no, no more feature flags.  Remember that those affect a *file*, not a *class*, which are
not necessarily 1:1.  

Besides, there's an opt-in way to skip the defaults already: Implement your own version.  The
argument that "an interface author might introduce a new method with a default that is not
quite perfect for every implementing class" never carried much weight with me, as it's a
fringe edge case, unlikely to happen, and easily resolvable when it does.  (Via the exact same means
as adding a new method without a default implementation.)

> *Complexity and Developer Experience:* While the feature could 
> significantly improve the developer experience, it also introduces 
> complexity in how interfaces are used. To alleviate this, the default 
> keyword could be explicitly used to mark default methods, making it 
> easier for developers to understand and manage. For example:
>
> interface I1 {  
> `    default public function foo(): int {  
>         return \PHP_INT_MAX;  
>     }  
> }
> `
> This explicit marking not only clarifies the intention behind the 
> method but also aids in distinguishing between regular and default 
> methods, simplifying the mental model required to work with interfaces.

How?  All I see here is another keyword I have to use.  It doesn't do anything extra for me as
a consumer of that interface.  It doesn't add disambiguation to the language, either for the
engine or human readers (as the presence of the body does that already).  It just gives me 8 more
characters to type.  



> Just thinking out loud here - looking forward to hearing some thoughts.

My thought is we should just pass Default Interface Methods and be done with it. :-)

--Larry Garfield


Thread (7 messages)

« previous php.internals (#125362) next »