Re: Pre-RFC proposal: opt-in implicit interfaces / structural typing
| From: | John Bafford | Date: | Fri, 07 Nov 2025 17:51:31 +0000 |
| Subject: | Re: Pre-RFC proposal: opt-in implicit interfaces / structural typing | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129133@lists.php.net to get a copy of this message | ||
Hi Spencer,
> On Nov 7, 2025, at 12:05, Spencer Malone <malone.spencer@gmail.com> wrote:
>
> Hey all! Long time browser, first time emailer. I wanted to start a pre-RFC discussion on the
> proposal of opt-in implicit interfaces / structural typing / "golang style interfaces".
>
> I have an early mostly working POC at
> https://github.com/php/php-src/compare/master...SpencerMalone:php-src:implicit,
> and was curious if this was an idea that could get any traction as an RFC?
>
> For those who don't want to open the link, it's essentially:
>
> ```
> implicit interface Drawable {
> public function draw(): void;
> }
>
> class Circle {
> public function draw(): void {
> }
> }
>
> assert((new Circle()) instanceof Drawable); // This would be true
> ```
>
> There is some previous art around here in the form of...
>
> https://wiki.php.net/rfc/protocol_type_hinting - I
> couldn't find any discussion on this other than reading that the author withdrew all their RFCs
> for unrelated reasons I think? Some of the core ideas here I think are still relevant, although in
> my implementation I chose to use syntax that adds a keyword onto the interface rather than at the
> call sites because I wanted to provide the ability for interface definers to keep their interfaces
> as explicit if they wanted, as well as... Well honestly, adding angle brackets around objects feels
> like a syntax misstep since that pattern is very associated with generics in my mind. I could be
> convinced of this though if that's the syntax people prefer.
>
> https://externals.io/message/115554#115603 - Convinced me that
> others may want this as well, although again that proposed syntax ended up stepping on the toes of
> attributes. I do think this does a good job of explaining _why_ I want this.
>
> - Spencer
I think this is an interesting idea, but personally, I would prefer to see classes always explicitly
declare their interfaces. An interface is a contract, and implementing the interface is agreeing to
conform to the contract. Just because a class has the shape of an interface doesn't mean it
follows the contract. implicit would probably be most useful for very simple interfaces, but those
are exactly the kind of interfaces that could be misinterpreted. For example, this poorly designed
class:
```
class DeckOfCards {
public ?Card $mostRecentlyDrawnCard;
public function draw() : void { ... }
}
```
should not be instanceof the Drawable interface, because it doesn't conform to the contract,
even though it structurally matches.
Perhaps slightly less torturously,
```
class Canvas {
public array $contents; //list<Drawable>
private CanvasInternalRepresentation $internalRepresentation
public function draw() : void { ... update the canvas's internal representation ... }
public function data($imageType) : string { .. } // return binary blob of $imageType data from the
internal representation
}
```
This Canvas takes a list of Drawables and uses a draw() method to update its internal
representation. But it itself is not Drawable (let's say, because Canvas can't be nested
for whatever reason).
In real code, there's probably a lot of things that structurally would conform to some sort of
simple Cache/Cacheable or Request/Response interface but for reasons wouldn't obey whatever
implied contract those interfaces would demand.
In my opinion, a better solution would be to allow for extensions that allow declaring interface
conformances. e.g.
```
extension Circle implements Drawable {
// We have now explicitly declared that Circle is a Drawable and conforms to its contract.
// This extension body is empty because Circle already has a conforming draw method,
// but if it didn't, we could add one here and it would be treated as part of the Circle
class.
}
```
Of course, this would be a much more involved proposal than implicit interfaces, which does have
elegance in its simplicity.
-John