Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108750 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 90408 invoked from network); 25 Feb 2020 06:28:22 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 25 Feb 2020 06:28:22 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1B059180212 for ; Mon, 24 Feb 2020 20:45:42 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS11403 66.111.4.0/24 X-Spam-Virus: No X-Envelope-From: Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 24 Feb 2020 20:45:39 -0800 (PST) Received: from compute7.internal (compute7.nyi.internal [10.202.2.47]) by mailout.nyi.internal (Postfix) with ESMTP id 4E18521E62 for ; Mon, 24 Feb 2020 23:45:39 -0500 (EST) Received: from imap26 ([10.202.2.76]) by compute7.internal (MEProxy); Mon, 24 Feb 2020 23:45:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:references :subject:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm2; bh=l5oj9DOa0o1TxYptFOYh3XtkzZWnYjtB7R2fshzOo vQ=; b=XR75YuBnLnlgcJGvZWVJYEx51v6WQl/V9no47iMjpxMz+HVxHxSdc/UKz KcH9F6ATXxRSQhXgzqoK+fuOqArSjbiPNyGx23ApBmKEB1noLtbz3uW+t7wMdODD wEgVkgxbRDQ/7ZpGTIxPzlBafTKZ7LYdTGyCX6wCYdRbGtlP8wIhPV9YBm9+e19y t8WvxWwX6AUSoC/2FmtHoRIaQ5szjY4mqCZFLvBXPweg37f79tADUNUt2B3oD71W H5fi4wgbDFV5idbYkQEz6IBLJrNlCDhJaeaOVixpkIKuakVRObn6RY/21pXFVujq djEE81iK0j3RXXt8TD9T2hdTjtwqw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedugedrledugdejgecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefofgggkfgjfhffhffvufgtgfesthhqredtreerjeenucfhrhhomhepfdfnrghr rhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtoh hmqeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehl rghrrhihsehgrghrfhhivghlughtvggthhdrtghomh X-ME-Proxy: Received: by mailuser.nyi.internal (Postfix, from userid 501) id B755B14200A2; Mon, 24 Feb 2020 23:45:38 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.1.7-947-gbed3ff6-fmstable-20200220v2 Mime-Version: 1.0 Message-ID: <726da829-6124-4331-8b28-4d654d652b2a@www.fastmail.com> In-Reply-To: <01FE7634-51EC-4D94-B4A0-62C477C096EA@newclarity.net> References: <8545d15e-ddd5-42be-8405-09697a077234@www.fastmail.com> <4d9688fe-cc57-44af-903e-05f4cbb1bbcc@www.fastmail.com> <452D962A-C588-4F04-B000-479EBEA9B9DB@newclarity.net> <9ebc648c-39d1-45b1-9f3d-9a799c6dec93@www.fastmail.com> <01FE7634-51EC-4D94-B4A0-62C477C096EA@newclarity.net> Date: Mon, 24 Feb 2020 22:45:18 -0600 To: "php internals" Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] [RFC] [DISCUSSION] Immutable/final/readonly properties From: larry@garfieldtech.com ("Larry Garfield") On Mon, Feb 24, 2020, at 3:25 PM, Mike Schinkel wrote: > > On Feb 23, 2020, at 12:06 PM, Larry Garfield wrote: > >=20 > > On Sun, Feb 23, 2020, at 2:39 AM, Mike Schinkel wrote: > >>> On Feb 21, 2020, at 6:17 PM, Larry Garfield wrote: > >>> I'm totally on board for better value object support, so that's a = good motive for me. The question I have is whether this is really a goo= d stepping stone in that direction or if it would lead down a wrong path= and lock us into too much TIMTOWTDI (for the Perl fans in the room). S= o let's think that through down that path. How would write-once propert= ies lead into properly immutable value objects? Or do they give us that= themselves? > >>>=20 > >>> The biggest challenge for immutable objects, IMO, is evolving them= . Eg, $result->withContentType(...) to use the PSR-7 example. Would we= expect people to do it with a method like that, or would there be some = other mechanism? If the properties are public, would we offer a more sy= ntactic way to modify them directly? > >>>=20 > >>> The with*() method style requires cloning the object. What happen= s to the locked status of a set property if the object is cloned? Are t= hey then settable again, or do they come pre-locked? > >>>=20 > >>> Neither of those seem good, now that I think about it. If they co= me pre-locked, then you really can't clone, change one property, and ret= urn the new one (as is the standard practice now in that case). If they= don't come pre-locked, then the newly created object can have everythin= g on it changed, once, which creates a loophole. I'm not sure what the = right answer is here. > >>>=20 > >>> My other concern is a public property (the most likely use case) w= ould have to be set in the constructor. If it's not, then callers canno= t rely on it having been set yet if it's set lazily. And if code inside= the class tries to set it lazily, it may already have been set by some = external code (rightly or wrongly) and cause a failure. > >>=20 > >>>=20 > >>> How do we address that? There's absolutely use cases where settin= g everything in the constructor ahead of time is what you'd do anyway, b= ut there are plenty where you wouldn't want to, either, which creates a = race condition for who sets it first, or tries to access it before it ge= ts set, etc. (This is where my repeated questions about lazy initializa= tion come from.) > >>=20 > >>=20 > >> I have struggled to follow this RFC thread fully, so if I am gettin= g=20 > >> something out of context, please note that and I apologize in advan= ce. > >>=20 > >> However, it would see that rules for `write once` properties to sup= port=20 > >> lazy loading would be rather simple: > >>=20 > >> 1. Write-once properties can only be updated once. > >> 2. Write-once properties can only be updated within the class where= =20 > >> they are declared. > >=20 > > This is the common use case I think many envision, but nothing in th= e proposal requires that. A public write-once property (as currently wr= itten) would be world-readable, and world-writeable, once. > > Separate visibility for internal and external access is a separate m= atter. (Also potentially useful, but not part of the write-once proposa= l at the moment.) >=20 > This just hit me, so I think I will mention it. =20 >=20 > The culture on the list seems not to be one of collaboration on RFC to= =20 > find solutions to improve PHP but instead waiting for someone to stick= =20 > their neck out with an RFC and then see who can shoot the most holes=20= > through it. >=20 > I did not actually expect that. I would have hoped for a collaborate=20= > culture of the nature I am used to in local startup-oriented meetups=20= > where most everyone is trying to help each other rather than just=20 > pointing out that the work someone else has done is deficient and not=20= > worthy of moving forward. Mike, Terje already responded here and he is spot on: "Destructive testing" is= a very good metaphor to use, and is what is happening in this thread. Evolving a language like PHP, with millions of users and billions of lin= es of code, is not, even a little, like a startup-oriented meetup. In a= meetup, "yes and" is a perfectly good model, and you can encourage ever= yone to make their own thing, throw it out into the market, and see what= happens. Multiple incompatible and competing startups can succeed and = the ecosystem is better for it. But there is only one PHP language, and throwing everything and the kitc= hen sink into it just to make people feel good and inspired is a superb = way to ruin the language. *Every* new feature or syntax addition create= s cognitive overhead for *millions* of people. If done in a way that ha= s lots of holes or flaws, it can create billion-dollar bugs that take a = decade to unravel, if ever. The cost of "getting it wrong" is high. Th= e cost/benefit analysis has to be very strong to justify adding syntacti= c weight to every PHP user in the world. That does not mean that we should oppose all change; hell, PHP is perhap= s the fastest evolving production language in the world right now. Ther= e's a long list of recent features I've supported and cheered for, and a= long list I'd still like to see. The same is true of almost everyone o= n this list. But in order to keep PHP a good, approachable, flexible language and kee= p it consistent (well, at least no more inconsistent than it already is)= , every one of those features needs to "run the gauntlet" of destructive= testing, poking holes in it, and finding all the gotchas. That's exact= ly the point: We *do* want to find all the little gotchas in a proposal,= now, before something gets into the language and the PHP-coding world f= inds them instead. Constructive nitpicking and destructive testing, in this context, are a = sign of respect. If folks on the list didn't respect this proposal, it = would have been met with "this is stupid", "we went over this, it's dumb= ", "why are you wasting our time?", or other such responses. (Certainly= there have been proposals met with such response in the past.) Or, sim= ply ignored outright. Instead, it's being met with "Hm, someone is goin= g to try and throw this against a wall harder than they have any right t= o; so let's do it now and see what happens so we can figure out how to m= ake it not shatter into a thousand pieces." That's a good thing. Numer= ous people are donating a not-small amount of their time to stress-test = the proposal. Some of that work has already improved the proposal, eg, the general agr= eement that it was best to just ignore untyped properties and references= entirely. That's a good thing. M=C3=A1t=C3=A9 has said that his end goal is immutable objects, and this= is a step in that direction. I fully support that goal. Is this actua= lly the right first step in that direction, or does it go down a wrong p= ath? I don't know yet; that's what all of the nitpicking and hypothetic= als and "running it over with a car to see what happens" is about: Figur= ing out if it is the right way to get to that goal. I'm glad we're havi= ng this conversation. Maybe a readonly flag only makes sense if we first/also add separate vis= ibility for getters and setters? I don't know, but it's worth asking. = Does it break cloning? Seems like it, but maybe there's a way around it= . Would some totally different approach get to a better end-state with = fewer edge cases? Could be. Those are realizations that only happen wh= en you take an RFC and "shoot the most holes through it", because then y= ou find the places where it's not bulletproof. That's the process worki= ng as designed. > > 1) Race condition if I assume that a public write-once property is a= materialized value, but access it before it gets materialized. >=20 > Can you please explain what "materialized value" means in this context= ?=20 > Are you speaking in Scala again? I don't speak Scala. If anything it's an SQL reference. A "materialize= d table" is basically a saved query result, which you can then reuse ove= r and over again, that self-updates when its underlying source changes. Think $user->fullName, where fullName is a public read-only property der= ived by concatenating $firstName and $lastName. If that is set in the U= ser constructor, great, no problem. If it's not, because it's not alway= s needed so you want to only do that work if it's going to be used, then= the caller doesn't know if it's already been set or if it's still undef= ined. Or, if public write isn't blanket-disallowed (which could be done= but I suspect would lead to other limitations), whatever internal code = would set the value doesn't know if it's already been set by an over-eag= er external routine (possibly incorrectly). > If you mean that a write-once property just has not yet been assigned,= =20 > I am not sure how that differs from just accessing a property that has= =20 > not yet been assigned today? It doesn't, which is one of the reasons public properties are broadly fr= owned upon today. However, most of the use cases of a readonly flag I c= an see apply to publicly-readable properties (which are therefore faster= to access than a method and involve less boilerplate code). So if a re= adonly flag doesn't resolve that problem, and in practice makes it a bit= worse, that puts it at a net-negative ROI. I keep coming back to supporting lazy-initialization because that seems = to me the best way to resolve that problem, and gives us some very clean= additional functionality. Are there other options that are better? Co= uld well be. Let's enumerate them and poke holes in all of those, too. > > 3) Cloning creates an interesting and complicated case of both of th= e above. Does a cloned object start with its write-once bits reset or n= o? There's problems both ways. >=20 > If there are problems, let us find solutions.=20 >=20 > 1. Only allow internal methods to change write-once properties. Possible; which then leads to the question "why can't every property hav= e asymmetric access?" Which someone else upthread mentioned, and IMO wa= rrants its own discussion. > 2. Options: > 2a. Extend clone statement to have `clone rewrite $foo` that would=20= > allow rewriting the write-once properties. In the common case, you'd only want to overwrite a subset of properties,= probably only one. How do you "unlock" just that one property? Is tha= t the right way to go about it? (I honestly don't know.) > 2b. Allow rewriting once of write-once object properties until some=20= > operation is performed to seal or lock the object (operation being a=20= > method call, a function call with the object as param, or a statement=20= > with the object as argument.) My preference here would be the "overwrite code block" Rowan suggested. = That may have other knock-on effects (either for consistency or engine = complexity) that we haven't thought of yet. > 2c. Don't allow rewriting of cloned objects. See if it is really a=20= > problem. Fix it in a future RFC if so. Possible, but also then removes one of the primary use cases for the fea= ture: Immutable value objects, since then evolving them becomes far more= work than it is now. --Larry Garfield