Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126039 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 0A2A71A00BD for ; Sat, 23 Nov 2024 20:28:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1732393863; bh=zFDQFnVyWBhbIvT+J3DbIyIxTgMlO3zzPLy0YVs9wtc=; h=Date:From:To:In-Reply-To:References:Subject:From; b=lbPWBVqK4hr8s/b+9t2lIYU+pL7r2z2zdIXUw2lXoS5v3dfFE3Vro3x069c5Odkxg 00diYkxavNQgkgJBk0ml3XB4tiCmq1XfOzEK+kvMF8zlX1eOgzzmVxJkiYlg2aO3Fh NIv46TckMd6Pia3DeHCJBGafpPTNG+QFtg99a636BT+l9UcQREObV3Q3OOPsp6kEty y8n2YzC95uSXKVE3YZMOfVBagRrhRMjBTU9KbFNDYd/e9Rt7bpaQipk0rz1LoTuZ8j 7abFzJVe4lh4kkbfwhP2zu46V4TsXRxuClx6q/09Ljk1Fo9YVBbcac+6VE5HVktcTu uQ1xVyhX7OlCA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 0F700180071 for ; Sat, 23 Nov 2024 20:31:02 +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,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout-b4-smtp.messagingengine.com (fout-b4-smtp.messagingengine.com [202.12.124.147]) (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 ; Sat, 23 Nov 2024 20:31:01 +0000 (UTC) Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfout.stl.internal (Postfix) with ESMTP id 153D0114012E for ; Sat, 23 Nov 2024 15:28:20 -0500 (EST) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-01.internal (MEProxy); Sat, 23 Nov 2024 15:28:20 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc: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=fm2; t=1732393699; x=1732480099; bh=MBOsXSFr1C qQUZY++bngacUJEbKWiSfwpfG5oEIgRfw=; b=Wjv3WDQVWrD3TAt1680aNCDqs9 yy5pkpC08VbxMPQUJQCM+BThCU7PLbKl/k6fxltwwEB5ehbxYe68dxo7iCwn5CMU GSiBCUX7/IbfGS1sGZd2RyEzaGgS6IaI+TOIF4rBWIQOHuOdAvL2G7qOSXEKidFn A2HMI145EQlueQDW4lDHKcVo1eCNtl59zKpBIanmZSeZxFZt2sqiJLGdgA2tiN7Q thq1TKSZZedlqxKplGn8QNXKjc9Frr2vUMHUGf/72Dv6/HfGruCkqZzB1OKWUVvH Yp7kYJeBe4ODpjPFGUyH6sn303vSOVILh6OJF8lCnsqy30pIU9igPp2SjDsQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc: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=fm1; t= 1732393699; x=1732480099; bh=MBOsXSFr1CqQUZY++bngacUJEbKWiSfwpfG 5oEIgRfw=; b=Mmte9wGL7geC8yKdcfc66yk3wIBGfDoHc57vxDSr6vDGC56Gq+5 +LoijEOWQDQLppKlGyKRoP8m/8O3Es9WYRfEA1rhiJXqxzCrtLTmr2EPMjqRRE+U 6+C3Ozf8MhVYedzhzkdGDl5f4bkF/hVOQVEF1GF9Xt9MooicV9uI7wkt/A7rwCNG /ckF2MkXfBp3KtQhfOQHQvYlsjl1kYHVi57M08VxnQ4YqHW4bvxF8ix7ZslbFV7v YQP+H9hvSzYw5p4h5xbbCKoO0UZ5m9UVVrwCAP9/Wm/7D7Fum5nwY4la78v5GmsO 0kTBgQTFaqFXK4VJzLAB/8noEui2sflnNCQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefuddrgedugddufeeiucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggvpdfu rfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefoggffhf fvkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhsfdcu oehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtthgvrhhnpedtueejtd ethfeulefhtdelieduteelffdtudelheffgedtieehhfelieejgfevgeenucevlhhushht vghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehrohgssegsohhtthhlvg gurdgtohguvghspdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuthdprhgt phhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 87198780068; Sat, 23 Nov 2024 15:28:19 -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: Sat, 23 Nov 2024 21:27:15 +0100 To: internals@lists.php.net Message-ID: <3e4506d7-227e-4521-a0c8-68e71fdc42a1@app.fastmail.com> In-Reply-To: <2c286462-a9ac-4b12-89bf-aea66680dea0@rwec.co.uk> References: <18b85ba5-5f1c-489c-9096-3ae203977fbe@app.fastmail.com> <163a08c1-2279-4a3f-a1bb-8cdfe406a4ba@rwec.co.uk> <0b87a3eb-7e63-4e28-a11d-25824e240a73@app.fastmail.com> <2c286462-a9ac-4b12-89bf-aea66680dea0@rwec.co.uk> Subject: Re: [PHP-DEV] [RFC] Data Classes Content-Type: multipart/alternative; boundary=af76a5a65d02442494fa1a85e9edd141 From: rob@bottled.codes ("Rob Landers") --af76a5a65d02442494fa1a85e9edd141 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sat, Nov 23, 2024, at 18:34, Rowan Tommins [IMSoP] wrote: > On 23/11/2024 16:05, Rob Landers wrote: >>> Your RFC doesn't discuss this - the changeName example shows behavio= ur >>> *inside* the method, but not behaviour when *calling* it >>=20 >> An interesting observation, can you explain more as to what you mean? >=20 > Looking closer, there's a hint at what you expect to happen in your Re= ctangle example: >=20 >=20 >=20 > $bigRectangle =3D $rectangle->resize(10, 20); > assert($bigRectangle !=3D=3D $rectangle); // true >=20 >=20 > It seems that modifications to $this aren't visible outside the method= , creating a purely local clone, which would be discarded if it wasn't r= eturned (or saved somewhere). >=20 > I can see the logic, but the result is a bit unintuitive: >=20 >=20 >=20 > data class Example {=20 > public function __construct(public int $x) {} > public function inc(): void { > $this->x++; > } > } > $foo =3D new Example(0); > $foo->x++; > $foo->inc(); > echo $foo->x; // 1, not 2 >=20 >=20 Interesting! I actually found it to be intuitive. Think of it like this: function increment(array $array) { $array[0]++; } $arr =3D [0]; increment($arr); echo $arr[0]; // is 0 We don't expect $arr to be any different outside of the function because= $arr is a value, not a reference. "data classes" are "values" and not r= eferences to values, thus when you modify $this, you modify the value, a= nd it doesn't affect values elsewhere. If you want to keep track of that= value, you have to put it somewhere where you can reference it=E2=80=94= a return value, global variable, property in a regular class, etc. In an= y case, lets keep going to see if there is a better way. > I think it would be clearer to prevent direct modification of $this: >=20 >=20 >=20 > data class Example {=20 > public function __construct(public int $x) {} > public function inc(): void { > $this->x++; // ERROR: Can not mutate $this in data class > } > public function withInc(): static { > $new =3D $this; // explicitly make a local copy of $this > $new->x++; // copy-on-write separates $new from $this > return $new; > } > } >=20 >=20 Not that I disagree (see the records RFC), but at that point, why not ma= ke data classes implicitly readonly? >=20 >=20 > That would still be compatible with Ilija's suggestion, which was to a= dd special "mutating methods": >=20 >=20 >=20 >=20 > data class Example { > public function __construct(public int $x) {} > public mutating function inc(): void { > $this->x++; > } > } > $foo =3D new Example(0); > $foo->x++; > $foo->inc!(); // copy-on-write triggered *before* the method is called > echo $foo->x; // 2 >=20 >=20 I actually find this appealing, but it is strange to me to allow this sy= ntax on classes. Is there precedent for that? Or is there a way we can d= o it using "regular looking PHP"; or are structs the way to go? Another alternative would be that mutations still trigger a copy-on-writ= e, but the outer variable is updated with $this upon return. So this wou= ld work: data class Example { public function __construct(public int $x) {} public function inc(): void { $this->x++; } } $foo =3D new Example(1); $bar =3D $foo; $foo->inc(); // foo is copied on mutation, and $foo points at the new va= lue on return. echo $bar->x; // 1 echo $foo->x; // 2 To me, this seems like it would be even more intuitive; $foo has the val= ue you would expect from outside the class and doesn't require you keepi= ng track of the value yourself. Though, there are some footguns here too: class Foo { Example $bar; function baz() { $bar =3D $this->bar; // should be $bar =3D &$this->bar $bar->inc(); echo $this->bar->x; // not incremented } } I could go either way on this one, honestly; so it makes sense to me why= structs would have a dedicated syntax for whichever you prefer. On the = one hand, it currently requires you to be explicit all the time (which c= an be annoying), and on the other hand, there's the implicit copy here, = which requires you to be explicit when you want a reference. I suppose there is a third option as well, and that is to not to any of = these options and just have data classes that always compare by value. > ??--=20 > Rowan Tommins > [IMSoP] =E2=80=94 Rob --af76a5a65d02442494fa1a85e9edd141 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Sat, Nov 23,= 2024, at 18:34, Rowan Tommins [IMSoP] wrote:
On 23/11/2= 024 16:05, Rob Landers wrote:
Your RFC doesn't discuss this - the changeName example= =0A shows behaviour
*inside* the method, but not = behaviour when *calling* it

An= interesting observation, can you explain more as to what=0A you = mean?

Looking closer, there's a = hint at what you expect to happen in=0A your Rectangle example:
=


$bigRectangle =3D $rectangle->resize(10, 20);
=
assert($bigRectangle !=3D=3D $rectangle); // true
<= p>

It seems that modifications to $this aren't visible outside= the=0A method, creating a purely local clone, which would be disca= rded if=0A it wasn't returned (or saved somewhere).

I can= see the logic, but the result is a bit unintuitive:


<= div>data class Example {
   public function __= construct(public int $x) {}
   public function = inc(): void {
     $this->x++;
=
  }
}
$foo =3D new Exampl= e(0);
$foo->x++;
$foo->inc();
echo $foo->x; // 1, not 2


<= div>
Interesting! I actually found it to be intuitive.
=

Think of it like this:

function increment(array $array) {
  $array[0]+= +;
}

$arr =3D [0];
<= div>increment($arr);
echo $arr[0]; // is 0
<= br>
We don't expect $arr to be any different outside of the fu= nction because $arr is a value, not a reference. "data classes" are "val= ues" and not references to values, thus when you modify $this, you modif= y the value, and it doesn't affect values elsewhere. If you want to keep= track of that value, you have to put it somewhere where you can referen= ce it=E2=80=94a return value, global variable, property in a regular cla= ss, etc. In any case, lets keep going to see if there is a better way.

I think it would be clearer to prevent direct modification of=0A $= this:


data class Example {
 =   public function __construct(public int $x) {}
&nbs= p;  public function inc(): void {
   =    $this->x++; // ERROR: Can not mutate $this in data class=
   }
   public functi= on withInc(): static {
      $ne= w =3D $this; // explicitly make a local copy of $this
&nb= sp;     $new->x++; // copy-on-write separates $ne= w from $this
      return $new;<= br>
   }
}



Not that I disagree (see the records RFC), b= ut at that point, why not make data classes implicitly readonly?


<= /p>

That would still be compatible with Ilija's suggestion, which was= =0A to add special "mutating methods":



<= /p>

data class Example {
    public fu= nction __construct(public int $x) {}
    p= ublic mutating function inc(): void {
   &= nbsp;    $this->x++;
   = }
}
$foo =3D new Example(0);
$foo->x++;
$foo->inc!(); // copy-on-write trigge= red *before* the method is=0A called
echo $foo->x= ; // 2



I actually f= ind this appealing, but it is strange to me to allow this syntax on clas= ses. Is there precedent for that? Or is there a way we can do it using "= regular looking PHP"; or are structs the way to go?

Another alternative would be that mutations still trigger a cop= y-on-write, but the outer variable is updated with $this upon return. So= this would work:

data class Example {
   public function __construct(public int $x) {}
   public function inc(): void {
 &n= bsp;   $this->x++;
  }
}

$foo =3D new Example(1);
$bar = =3D $foo;
$foo->inc(); // foo is copied on mutation, an= d $foo points at the new value on return.
echo $bar->x;= // 1
echo $foo->x; // 2

T= o me, this seems like it would be even more intuitive; $foo has the valu= e you would expect from outside the class and doesn't require you keepin= g track of the value yourself. Though, there are some footguns here too:=

class Foo {
  Example $= bar;

  function baz() {
=     $bar =3D $this->bar; // should be $bar =3D &$this-&= gt;bar
    $bar->inc();
  =   echo $this->bar->x; // not incremented
 = }
}

I could go either way on= this one, honestly; so it makes sense to me why structs would have a de= dicated syntax for whichever you prefer. On the one hand, it currently r= equires you to be explicit all the time (which can be annoying), and on = the other hand, there's the implicit copy here, which requires you to be= explicit when you want a reference.

I supp= ose there is a third option as well, and that is to not to any of these = options and just have data classes that always compare by value.

??--=20=0ARowan Tommins=0A[IMSoP]

=E2=80=94 Rob
=
--af76a5a65d02442494fa1a85e9edd141--