Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129134 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id D25F41A00BC for ; Fri, 7 Nov 2025 18:23:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762539822; bh=/uUQqOvIBPZcKgkZBxalkA6a1NrDC1+dIPkqmYNbOCg=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=hICSRewF+KqArS6oLGAkTEsCGkf8CiVNtL4rKuiL2PSztDk7jlaTLWg0LKhJmSTD1 SA87rQGzPl8Q9UjFHnOhDxJN5kwcueDsoQE3AjRlvy9fcnonwhGCCwB7jUe4XYOao4 l8i9bnVLY2JRnxXwu/r7MdGnUWvIuPmldyOKZYxMr/r044IWuCDNOe/oh5Rm+0Ewyl Do5hfY5/U+c0HzKpo5SIQ4hgA+gjeMlDlGo9KKVNiZz7KqGuj8YetyXW8syKGhetWl BA74e9x6pi/Zbgky75J+ZXQFeyNsE06UPIgubYlMcSNopod2m2H7c8MDvO1lCO50jZ 6FEjYlOoNk+Ig== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 777BD1801D6 for ; Fri, 7 Nov 2025 18:23:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.7 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,MIME_BOUND_DIGITS_15,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-il1-f174.google.com (mail-il1-f174.google.com [209.85.166.174]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 7 Nov 2025 18:23:41 +0000 (UTC) Received: by mail-il1-f174.google.com with SMTP id e9e14a558f8ab-4330dfb6ea3so3923045ab.0 for ; Fri, 07 Nov 2025 10:23:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762539815; x=1763144615; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=ZTz1ihTsnB5bRYTw6Jb07pxCP08QEWRmq4yDjTXlJ4E=; b=neRr5s1dPLqygu/D3fDw7c8I0jexXpSarDecg8JSFB49HBxLJYKjyj2STGgLPWfO1A JBTwf1jxQThPlNK3kVL5SKWYlZtNC61RJJMyiF4lKkIiiIFacBv2DWqEcSiFmt6HwQ35 KPzFTKoBfA1+d03bK1tYehkAddlJSCWx0AR+tr6KIcYpNWg1TbVbmHO67T54M+Lk5EGu RGNbfYs5MBSNjH0AslIqJ/ECIg/VtdlQW9O+ZdXpqHwj4y4dEDZuGIc8dFOk5Zxq48Xz qpjYm57M0dlPM0V5a5ytxMS4OdAcR/tUUsU0gD2Mc5Ar/LAikIfXYDx+OyTaYI8JSzMR 100g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762539815; x=1763144615; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=ZTz1ihTsnB5bRYTw6Jb07pxCP08QEWRmq4yDjTXlJ4E=; b=Z4MAXgiE9WylnFCiuiMukDKYKW0K1Viw4Z77IH871VQEHltQkMgby2EQQnWq1UXwP7 WEPHJkaz7j0Cxnz7HR52zlW9GX8iJGoRGE0ba9jV341T/sDWHtgqhHQgJxMSLUQHPKVj EFlz2Ejg+gGP/stKyUuaW/e9Z7l6ZtvhSQ+36f4la9S2h3FtpKjvdVIZQR+8h4SgD54L Y6gTomNo/HLJRbC4ui0B1a3m/2dCtYTnxtH/0buEsPvjeKUZ+cqs0qf2DLXs+NSZhSlr cxxjlAVx3o2RpKlvoa9NiQknHkyrMPg07FFcI5jBFFHkazPZ0/T85NNe1cQIXKH8XkaV 2RPw== X-Gm-Message-State: AOJu0Yz+HZ5uqXeoyQ7Xwz7kggC/03DtLJH0jwb/Ffa2H0zA566xL1B8 x0bY5yT700yqs58mr0UbwaJgT9rFirZbfNImfBukQjfaBPS0ezrk8aDjYCF2zYHHbs5CS1wGFjE 86Tl85M4wY59sWeUCFNTaKcbdU+LRIwKkJA== X-Gm-Gg: ASbGncsTWGU9LV8Yd1cd4cF+vY8svSE1o8j4HES89sCU83ecYCv8aaS2FFY1cEf5vWi 1zKvanpDbS/Fwbp1/NavSJ3ggdWd+0lPsYEXUMJYBJq42FJUyP+IfLYmgvptlqJucJ8v+IhPN5e +JW+1ZWjKnfaGJLnSY+eVggFSGT2AouprYFDHQToPp28Xsk3kzJ2g3utVB/u9Hn8tI+lmg0Furo OXCI+qZh5FVFAVsme8FKusqaIW48k08ECAZxcFdY7qXF1NIscIggh1xELnSfi2jGuUDD1I47qRP kJFk0DM1 X-Google-Smtp-Source: AGHT+IHuUvVquaBYGlO0MHEpvKQRxKlqYdRWKoJ+cX8BouZyEaZNklO1u86NCBevu0XVZ97V/y8iDi/ifAB1nU093hA= X-Received: by 2002:a05:6e02:3387:b0:433:27e9:8690 with SMTP id e9e14a558f8ab-43367e83fb3mr5449575ab.21.1762539815181; Fri, 07 Nov 2025 10:23:35 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: <83BA9524-6095-4C8B-8F82-B2F344C4D062@zort.net> In-Reply-To: <83BA9524-6095-4C8B-8F82-B2F344C4D062@zort.net> Date: Fri, 7 Nov 2025 10:23:24 -0800 X-Gm-Features: AWmQ_bki3lIjqLmRu_gxo6IZP-PSLVbeAxyo1mNE2NviJzQJ2O0hlSjp_G1RwQk Message-ID: Subject: Re: [PHP-DEV] Pre-RFC proposal: opt-in implicit interfaces / structural typing To: John Bafford Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="0000000000009624700643054704" From: malone.spencer@gmail.com (Spencer Malone) --0000000000009624700643054704 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable The extension idea is interesting! I hadn't thought of that. Autoloading might be a PITA with that, I'm trying to think of how you would manage that= ? On the subject of "mistaken typing" or implicit interfaces being met by classes that don't actually do the desired behavior, I think you're right it's a risk, my counterargument is typescript and golang both have implicit interfaces in some form (well typescript calls them structural typing), but I don't think in either language I've ever really run into a case where that risk becomes a reality. I'm hesitant to declare this because I'm not sure this is the best way to frame this from a PR perspective, but in many respects this proposal is viewable as a syntax shortcut to duck typing, which is already pretty common across PHP. While there are definitely drawbacks to duck typing, it's still a very useful tool IMO (and becomes very cumbersome if you need to duck type a larger set of functions). On Fri, Nov 7, 2025 at 9:51=E2=80=AFAM John Bafford wro= te: > Hi Spencer, > > > On Nov 7, 2025, at 12:05, Spencer Malone > 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:imp= licit, > 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 a= re > 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, addin= g > 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 se= e > 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 coul= d > 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 > 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 treate= d > 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 > > --0000000000009624700643054704 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
The extension=C2=A0idea is interesting! I hadn't = thought of that. Autoloading might be a PITA with that, I'm trying to t= hink of how you would manage that?

On the subject = of "mistaken typing" or implicit interfaces being met by classes = that don't actually do the desired behavior, I think you're right i= t's a risk, my counterargument is typescript and golang both have impli= cit interfaces in some form (well typescript calls them structural typing),= but I don't think in either language I've ever really run into a c= ase where that risk becomes a reality. I'm hesitant to declare this bec= ause I'm not sure this is the best way to frame this from a PR perspect= ive, but in many respects this proposal is viewable as a syntax shortcut to= duck typing, which is already pretty common across PHP. While there are de= finitely drawbacks to duck typing, it's still a very useful tool IMO (a= nd becomes very cumbersome if you need to duck type a larger set of functio= ns).

On Fri, Nov 7, 2025 at 9:51=E2=80=AFAM John= Bafford <jbafford@zort.net>= wrote:
Hi Spenc= er,

> 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 pr= e-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...Spence= rMalone:php-src:implicit, and was curious if this was an idea that coul= d get any traction as an RFC?
>
> For those who don't want to open the link, it's essentially: >
> ```
> implicit interface Drawable {
>=C2=A0 =C2=A0 =C2=A0public function draw(): void;
> }
>
> class Circle {
>=C2=A0 =C2=A0 =C2=A0public function draw(): void {
>=C2=A0 =C2=A0 =C2=A0}
> }
>
> 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 c= ore 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 t= he call sites because I wanted to provide the ability for interface definer= s to keep their interfaces as explicit if they wanted, as well as... Well h= onestly, adding angle brackets around objects feels like a syntax misstep s= ince that pattern is very associated with generics in my mind. I could be c= onvinced of this though if that's the syntax people prefer.
>
>
https://externals.io/message/115554#115603 - Conv= inced me that others may want this as well, although again that proposed sy= ntax ended up stepping on the toes of attributes. I do think this does a go= od 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 contr= act, 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 fol= lows the contract. implicit would probably be most useful for very simple i= nterfaces, but those are exactly the kind of interfaces that could be misin= terpreted. For example, this poorly designed class:

```
class DeckOfCards {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 public ?Card $mostRecentlyDrawnCard;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 public function draw() : void { ... }
}
```

should not be instanceof the Drawable interface, because it doesn't con= form to the contract, even though it structurally matches.

Perhaps slightly less torturously,

```
class Canvas {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 public array $contents; //list<Drawable><= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 private CanvasInternalRepresentation $internalR= epresentation
=C2=A0 =C2=A0 =C2=A0 =C2=A0 public function draw() : void { ... update the = canvas's internal representation ... }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 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 it= s internal representation. But it itself is not Drawable (let's say, be= cause 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 interfac= e but for reasons wouldn't obey whatever implied contract those interfa= ces would demand.


In my opinion, a better solution would be to allow for extensions that allo= w declaring interface conformances. e.g.

```
extension Circle implements Drawable {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 // We have now explicitly declared that Circle = is a Drawable and conforms to its contract.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 // This extension body is empty because Circle = already has a conforming draw method,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 // 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 interf= aces, which does have elegance in its simplicity.

-John

--0000000000009624700643054704--