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?
=
div>
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--