Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125977 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 qa.php.net (Postfix) with ESMTPS id A10471A00BD for ; Sun, 17 Nov 2024 07:22:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1731828299; bh=RtKMXdokea6lnPa/BIyXUPJePl4soycx6X59bw2RABQ=; h=Date:From:To:In-Reply-To:References:Subject:From; b=TxHoxg/TvXAtO8xSUMfoXCN2LLzl4csJj/GOCvas8sImNYnaqXGIQKaPdRTk48dso K+XsIh12C2h3od8qI7vwy2JOYx3Vgw7UGTG0f2IXj7T5nS4H/dFyDIxOazWwbifgki 8y4KWg/T002FUyUfRmhdM9ymsQtOba9QN2CSrvoIfRloRp+V+jnUdrIezhTns/cXAG WlgqmAskAxLCEx2vnUD+mFdlnBVgpsoBpR0EpSLT14IPqgYsNdmucBI6yIx9tn0XbB k2KD0Q/gdImaP1AOyG7qlcd6SttFXhT5jQf+S3SnF5VsQSLZAh50pkeKrAed7OmjbH DbtRZlLsrblkQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 04C2C18004B for ; Sun, 17 Nov 2024 07:24:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-a2-smtp.messagingengine.com (fhigh-a2-smtp.messagingengine.com [103.168.172.153]) (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 ; Sun, 17 Nov 2024 07:24:57 +0000 (UTC) Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfhigh.phl.internal (Postfix) with ESMTP id 3ABE111400DA for ; Sun, 17 Nov 2024 02:22:19 -0500 (EST) Received: from phl-imap-06 ([10.202.2.83]) by phl-compute-01.internal (MEProxy); Sun, 17 Nov 2024 02:22:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm3; t=1731828139; x=1731914539; bh=uDbQBcedQgnl2avJv6q+4 OgOd+7xViKeGV7WhuUFsNY=; b=DJ1idIdJPsG7tj/m4hCEnkDmEGLZXqCm56bPv Op/VAQS1YwXPeKSzW+PvjsaVQnM7OIFKyrMA7xMaXE9AdtW4/dKTdiF/WI6u2Yhj LrM7CyQorb8W8YUncj1fu+N+ixFfhtwo46SHfDK3AJA78RsH8QEESoivKN17I4cu EIHKqkeEK4VaXB9MJRfUCVaTfUfRx7pPGHWJUm1iFw/oMCTYexlHlB5jGN9Q1oDd sDrPbzMcqOxKXW1bz2aPd4s3xaTAJpEw/qFafr8ZGPNBzWULqgL1q1pjI8OzVcIO tNyV1bY+wr2VFb6oa4UnpMGdrueB6FcrmGX3qB4iF0SZYjhIA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1731828139; x=1731914539; bh=u DbQBcedQgnl2avJv6q+4OgOd+7xViKeGV7WhuUFsNY=; b=E1497tMubt+HUaSR4 aAuxmTyzun9M0PrCWRPII0KdMAhkCjlPjdHdMapJL7ZQKBi2YUP04wtSeKJzLj5C oAMPFxzjcG/ealb9K240dd5t9b1kAhz7VS/U2jUSPBX0gqyP8uIlQmDrMxLcBIs5 aC866SG87rYEqUenM0RE6U7TpDiPXbkf/C+EUxAnyuFppmynheyhtqyrnhq0xZ7K bER88+NJstTVXtovfCOOPwrMXsFnhoRHYr312HLTPzo2bReg3vyrUMC0C0Loby0r ZSEt9EOf9phOv3vKNIg7lJkf0ANR7qK3EzTYV1/IwamQdQYSQhQULwGRRS6qEw8i jlBuQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefuddrvdejgddutdegucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggvpdfu rfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnh htshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtgfesthhqredtredtjeen ucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfih gvlhguthgvtghhrdgtohhmqeenucggtffrrghtthgvrhhnpeeugfetieejueevffdulefh hfethfekvedtueevgfffvdefiedvtefgheevteelffenucffohhmrghinhepphhhphdrnh gvthenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehl rghrrhihsehgrghrfhhivghlughtvggthhdrtghomhdpnhgspghrtghpthhtohepuddpmh houggvpehsmhhtphhouhhtpdhrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdr phhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id DB40429C006F; Sun, 17 Nov 2024 02:22:18 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Sun, 17 Nov 2024 01:21:29 -0600 To: "php internals" Message-ID: <68eba37e-1bfe-4509-af41-112bb196415c@app.fastmail.com> In-Reply-To: References: Subject: Re: [PHP-DEV] RFC: Records Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Sat, Nov 16, 2024, at 5:14 PM, Rob Landers wrote: > Hello internals, > > I'm ready as I'm going to be to introduce to you: "Records"=20 > https://wiki.php.net/rfc/records! > > Records allow for a lightweight syntax for defining value objects.=20 > These are superior to read-only classes due to having value semantics=20 > and far less boilerplate, for most things developers use read-only=20 > classes for. They are almost as simple to use as arrays (and provide=20 > much of the same semantics), but typed. > > As an example, if you wanted to define a simple User record: > > record User(string $emailAddress, int $databaseId); > > Then using it is as simple as calling it like a function, with the & s= ymbol: > > $rob =3D &User("rob@bottled.codes", 1); > > Since it has value semantics, we can get another instance, and it is=20 > strongly equal to another of the same parameters: > > $otherRob =3D &User("rob@bottled.codes", 1); > assert($otherRob =3D=3D=3D $rob); // true > > Records may also have methods (even hooks), use traits, and implement=20 > interfaces: > > record Vector3(float $x, float $y, $z) implements Vector { > use Vector; > public float magnitude { > get =3D> return sqrt($this->x ** 2 + $this->y ** 2 + $this->z ** 2) > } > } > > Further, an automatic (but overridable) "with" method is generated for=20 > every record. This allows you to get a new record similar to a given=20 > one, very easily: > > record Planet(string $name); > > $earth =3D &Planet("earth"); > $mars =3D $earth->with(name: "mars"); > > The depth of records was an immense exploration of the PHP engine,=20 > language design, and is hopefully quite powerful for the needs of=20 > everyday PHP and niche libraries. I took care in every aspect and trie= d=20 > to cover every possible case in the RFC, but I still probably missed=20 > some things. I plan on having a full implementation done by the end of=20 > the year and open to a vote by the end of January, but I'd like to ope= n=20 > the discussion up here first. Love it or hate it, I'd like to hear you= r=20 > thoughts. > > =E2=80=94 Rob Hi Rob. I appreciate the amount of work that's gone into this, and I share most = of its spiritual goals. However, as I have discussed in various threads= before, I believe a separate value type is the wrong approach to take. One of my guiding principles is that features should be focused, targete= d, and designed to integrate well with other features. (That doesn't al= ways mean small, just focused.) It should be easy for devs to cleanly c= herry pick what features they want to use, and not be forced into all-or= -nothing situations where it can be avoided. That's why I do not believe bundling a bunch of object-ish features into= a new construct that is almost but not quite an object is wise. Every = one of those features I can see wanting to use stand-alone on a regular = object. I see these features collected here: * Immutable object * Inline constructors * value-style passing * dedicated evolvable syntax (the with() method) * alternate creation syntax (&RecordName) * value-based strong-equality That's a half-dozen features that I can see a good argument for wanting = on objects, without all the others. As Ilija notes, immutable objects are not always the answer. I like the= m, and use them frequently, but they're not always appropriate. And we = already have them with either readonly classes or now private(set) (whic= h is close enough to immutable 99.4% of the time) I can see the benefit of an inline constructor. Kotlin has something si= milar. But I can see the benefit of it for all classes, even service cl= asses, not just records. (In Kotlin, it's used for service classes all = the time.) There's already been an RFC for clone-with that works on any object; it = just never made it to a vote. I could see an argument for an even more = dedicated syntax (eg, eliminate "clone" and just do "$foo with (bar: 'ba= z')"), but again, useful on all objects, not just records. The alternate creation syntax... OK, this one I can't really see a benef= it to, and Ilija already noted it may cause conflicts. Value-based strong equality, in cases where I want that, I also want to = be able to control it. That goes back to Jordan's operator overload RFC= , and specifying a custom =3D=3D and <=3D>. I'd rather just have that. Value-style passing is the really interesting one, but I want to be able= to use it without being forced into all of the other features here. Eg= , I could easily see wanting to have a value-style-passing mutable objec= t. I do all kinds of in-place mutation in my function, then pass it on = to something else, and because that's a function boundary it gets cloned= (either immediately or later on modification) automatically for me. So what I see here is 4 different RFCs (value-passing, inline constructo= rs, evolvable syntax, more robust object equality) that should stand on = their own, for any object, so that I can pick and choose which I want a = la carte. Giving me an fixed combination of them I cannot modify is not= helpful. I would probably support all four of those as stand-alone RFC= s (I'm still undecided about inline constructors, but could be talked in= to them). Plus, having another fixed type creates questions any time a new feature= is added. Not all object features are available for Records.. So if w= e add a new object feature, should Records get it? Eg, the RFC has seve= ral very good examples of leveraging property hooks in a Record. Suppos= e that Records existed first, before hooks were introduced. Then we hav= e to debate "so do hooks make sense on Records, too?" (And you know tha= t bikeshed would be polkadotted by the time we're done.) For that matte= r, can I specify asymmetric visibility on a Record? Do we even want to = have that debate? (I don't, honestly.) I would far prefer assembling record-ish behavior myself, using the smal= ler parts above. Eg: final readonly data class Point(int $x, int $y); "final" prevents extension. "readonly" makes it immutable. "data" give= s it value-passing semantics. Any class can use an inline constructor. = "with" is designed to work automatically on all objects. Boom, I've ju= st assembled a Record out of its constituent parts, which also makes it = easier for others to learn what I'm doing, because the features opted-in= to are explicit, not implicit. So I don't think I can support a fixed bundle like this, but I would hap= pily support the individual constituent features on their own. --Larry Garfield