Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129133 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 294591A00BC for ; Fri, 7 Nov 2025 17:51:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762537908; bh=HnfkUwbOn8/D595TOBvNSb6/HZXx7XmgJnUnkMKcrN8=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=Dg14hy6hlgCi0QDVoQgiwToakQ4ZeS3fNsta0CEnjj2+kGAXkTISzHanhsPRlnrjM +lfwxk/rH5BjvFGS1vEX1RO7K5uYmxKookr2WZX4NM8eVqb21PQPJTPR+Zx7sP68zL 1ZagsUxPkGbjpr2b8G6szIyHA5i8mOkOB/k3yF98wMR6VfQUHTBXqehYlyUi1LmLht AdVMs/pTegvDeVNZhfXhEL+JrLuBezgDdS8bxv7aweb/2xtnRkAaOIc0a6YnKshYHi UUXr6dBS7M3mGr33cIdQKvGrYeMfyWviDo+Lkwf3dVEbsyvKBvv6kYttt6FsaXJMHY c4faHiNlFId4g== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id DE2D6180086 for ; Fri, 7 Nov 2025 17:51:47 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from nebula.zort.net (nebula.zort.net [96.241.205.3]) (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 17:51:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=zort.net; s=zort; t=1762537902; bh=HnfkUwbOn8/D595TOBvNSb6/HZXx7XmgJnUnkMKcrN8=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=q82lacCvF7L+5qWHH2BOhOnYS8WBt6uIPni9c8Tr/8kPs6AgywdW0FLPl7c9hHRE0 K2yCqwCZrESe0p5puhqGsUdvwQTY1Qgoj32U8uh/FJQMODKA7Mf+cVaZeQhnKgRuyJ vwq2qFq2k6XRk7OeJ5wUNmuHf4Ny7hJS9R19vvPs= Received: from smtpclient.apple (pulsar.zort.net [96.241.205.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by nebula.zort.net (Postfix) with ESMTPSA id 122581300509; Fri, 7 Nov 2025 12:51:42 -0500 (EST) Content-Type: text/plain; charset=us-ascii Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.700.81\)) Subject: Re: [PHP-DEV] Pre-RFC proposal: opt-in implicit interfaces / structural typing In-Reply-To: Date: Fri, 7 Nov 2025 12:51:31 -0500 Cc: internals@lists.php.net Content-Transfer-Encoding: quoted-printable Message-ID: <83BA9524-6095-4C8B-8F82-B2F344C4D062@zort.net> References: To: Spencer Malone X-Mailer: Apple Mail (2.3826.700.81) From: jbafford@zort.net (John Bafford) Hi Spencer, > On Nov 7, 2025, at 12:05, Spencer Malone = wrote: >=20 > 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". >=20 > I have an early mostly working POC at = https://github.com/php/php-src/compare/master...SpencerMalone:php-src:impl= icit, and was curious if this was an idea that could get any traction as = an RFC? >=20 > For those who don't want to open the link, it's essentially: >=20 > ``` > implicit interface Drawable { > public function draw(): void; > } >=20 > class Circle { > public function draw(): void { > } > } >=20 > assert((new Circle()) instanceof Drawable); // This would be true > ``` >=20 > There is some previous art around here in the form of... >=20 > 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. >=20 > 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. >=20 > - 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 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