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.


In the end, the goal of this RFC would be to promote a “new way” of
inheritance, which is described in depth in that post I mentioned earlier
[1]. It’s a different programming style, but I think there are good
arguments to be made for it, even though it will likely not be everyone’s
cup of tea. Modern languages like Rust and Go show that there’s merit in
rethinking classical inheritance, and there are signs that the PHP
community is open to it as well, given the examples with Laravel’s default
implementation traits, static analyser support, as well as the interface
default methods RFC last year.

If internals are open to the idea, I would like to draft a proper RFC for
it. Please let me know your thoughts!

Brent

Links:


   -

   [1] https://lwn.net/Articles/548560/
   -

   [2a]
   
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Auth/Authenticatable.php
   -

   [2b]
   
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Contracts/Auth/Authenticatable.php

   -

   [2c]
   
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Contracts/Auth/MustVerifyEmail.php
   -

   [2d]
   
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Auth/MustVerifyEmail.php
   -

   [3a]
   
https://phpstan.org/writing-php-code/phpdocs-basics#enforcing-implementing-an-interface-for-traits
   -

   [3b]
   
https://psalm.dev/docs/annotating_code/supported_annotations/#psalm-require-implements
   -

   [4] https://wiki.php.net/rfc/interface-default-methods

Reply via email to