Newsgroups: php.internals
Path: news.php.net
Xref: news.php.net php.internals:125979
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 D67CC1A00BD
for ; Sun, 17 Nov 2024 23:13:09 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail;
t=1731885347; bh=NhaPmUiGoc5czuqMUp3e2JC/am/Gc0g/Bm1ezvmJWGQ=;
h=Date:Subject:To:References:From:In-Reply-To:From;
b=h8/ZWXqlPlUho8g71GqlfqIylxSEgUUHlusGPXknOCLEX0NCZ0fVPOFrEAyGKfNxM
6Ux/kviHyOMe1NJ/BjqsB5dfd2ilF4f+viqm9Vv1tFPwO1SLtMwD6eE9HtfvXnbb7M
hN8yjbpe1tp5u7uLL0c5hpZV/M7mKhaT0VKCbXpnUXDoGDgMM/hFCXfdet/0wdOsjw
e/h0/RBHqmGJTt1AUk3hrYjsITxZ30LMeBIOZQB9pNO/H8N5rmTbz7LA/rquuVE/7W
6KAF4HBANz4HMow/xSAZQBTuX3wUkv/TuQOqnA9ISFnZyRxDzU850hYEZJ/0Is2hM1
xZTiMLwbEty3g==
Received: from php-smtp4.php.net (localhost [127.0.0.1])
by php-smtp4.php.net (Postfix) with ESMTP id B51F818003E
for ; Sun, 17 Nov 2024 23:15:46 +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_VALIDITY_CERTIFIED_BLOCKED,
RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED,
SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0
X-Spam-Virus: No
X-Envelope-From:
Received: from fout-b5-smtp.messagingengine.com (fout-b5-smtp.messagingengine.com [202.12.124.148])
(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 23:15:46 +0000 (UTC)
Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44])
by mailfout.stl.internal (Postfix) with ESMTP id A94521140108
for ; Sun, 17 Nov 2024 18:13:07 -0500 (EST)
Received: from phl-mailfrontend-01 ([10.202.2.162])
by phl-compute-04.internal (MEProxy); Sun, 17 Nov 2024 18:13:07 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rwec.co.uk; 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=fm3; t=1731885187; x=1731971587; bh=CuHfmt9Qcm
eGmCgYHKJ2nM65v5tiB4McCTjqm62O+KA=; b=BcAEv88wR08zXqL1qeyfykf5Ub
oIDm65ayvbDGKOZDFVknuEKH8F55Dyb3sYwzQx3i5udayLm3ZB15s1h0aj6xkujM
n4OZN1+/846LuFdw6skx75jTMUd6V19VixSazHfl/VE2QaPN2UX0xQ6i6mUbxJMi
ybIXr7u4jMo0EGey9dK0hFcg0NJseKz0PLemkz7vM0B+GfY7wgeUx9BCvDJQlmVk
xZAsnOJNQqiuRxfbAfAhc/AZwwlHtkz2gh509dSrRtMkIxkAvrKIl09NFujO7sIq
dJemzDBtIncc0pRfJhj8UFnDJkhiSBvKwRfivxpeFXrNhhyM+XXiaDHQRcqQ==
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=fm3; t=
1731885187; x=1731971587; bh=CuHfmt9QcmeGmCgYHKJ2nM65v5tiB4McCTj
qm62O+KA=; b=Iy7FZcJrq6PsHvrMcRUlXHvaR8HpeoX9K27msJUvUYBBghFiMtl
/Ow0847gF0qyTRZ6aW2OrZwctr5tCiYH6LqJ7Y/HFFLcVt1mih2M7ZLJzQkbMxlP
YkIzlJW3kzp34sRz1TAF0IEuzsBfkkL8fmOLBe0GK6jx+KWdSK9D3nVkvhE93N8R
Zp/fEWhy/q01iZd/BSGgbWp43woeaoUAH8cJLn25m7GE2royRxlT+0tudOiH+Jge
0rWC5FUbnbPL/aHoHhjm9Wk6VDXQ94iSzeEEKIrxHU0LliP5BPQlKKs5AC33Bd/C
U94JLnVZvhnLGTLC61zG5CbbPErVa1B9DeQ==
X-ME-Sender:
X-ME-Received:
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefuddrvdelgddtkecutefuodetggdotefrodftvf
curfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdpuffr
tefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecunecujfgurheptgfkffggfg
fuvfhfhfgjsegrtderredtvdejnecuhfhrohhmpedftfhofigrnhcuvfhomhhmihhnshcu
lgfkoffuohfrngdfuceoihhmshhophdrphhhphesrhifvggtrdgtohdruhhkqeenucggtf
frrghtthgvrhhnpeehteelieeigfeuudeiueeiffdvveehudeufeekjeeugffffedtiedt
geettdelteenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh
hmpehimhhsohhprdhphhhpsehrfigvtgdrtghordhukhdpnhgspghrtghpthhtohepuddp
mhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtsh
drphhhphdrnhgvth
X-ME-Proxy:
Feedback-ID: id5114917:Fastmail
Received: by mail.messagingengine.com (Postfix) with ESMTPA for
; Sun, 17 Nov 2024 18:13:06 -0500 (EST)
Content-Type: multipart/alternative;
boundary="------------rQWv1780KumNh803ioZG0gL4"
Message-ID: <9e45ff96-7388-466e-813a-74e0dd2700af@rwec.co.uk>
Date: Sun, 17 Nov 2024 23:13:03 +0000
Precedence: bulk
list-help:
list-post:
List-Id: internals.lists.php.net
x-ms-reactions: disallow
MIME-Version: 1.0
User-Agent: Mozilla Thunderbird
Subject: Re: [PHP-DEV] RFC: Records
To: internals@lists.php.net
References:
<68eba37e-1bfe-4509-af41-112bb196415c@app.fastmail.com>
Content-Language: en-GB
In-Reply-To:
From: imsop.php@rwec.co.uk ("Rowan Tommins [IMSoP]")
This is a multi-part message in MIME format.
--------------rQWv1780KumNh803ioZG0gL4
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
Hi Rob,
I'm torn on this one. On the one hand, it does look like a nice solution
for adding custom value objects to the language; on the other hand, it's
a lot of things that are "just slightly different" for users to get used to.
On 17/11/2024 21:30, Rob Landers wrote:
> One of the main reasons for the alternative creation syntax is because
> I felt that "new" was misleading at best, and just plain wrong at
> worst. It is also why I chose "&", to make it clear you are not
> getting "a new one" but one that "just happens to exist with the
> values you asked for." I'd be open to a different keyword or something
> else entirely. It's just that "new" is the wrong one for records.
I'm not convinced by this, because I'm not convinced the "mental model"
in the RFC is the one that most developers need to care about. I think a
much simpler mental model (for someone who understands the rest of PHP) is:
- records are copy-on-write (like arrays, not like objects)
- the === operator returns true for two records with the same value
(again like arrays, not like objects)
- the implementation optimises two records with the same values to share
memory
The third point is useful to know if you're creating a lot of them, but
probably irrelevant most of the time.
We also might not want to make it a hard guarantee, because there may be
cases where a different trade-off is more efficient. For instance, maybe
we will optimise $foo->with(a: 1)->with(b: 2)->with(c: 3) to overwrite
values in-place, at the cost of an extra condition in the `===`
implementation.
That would be similar to some of the changes to zvals in PHP 7; for
instance, "$foo=42; $bar=$foo;" will copy the value 42 to a new piece of
memory, not increase a zval reference count as PHP 5 would have done.
That leaves the mental model as mostly "records are a bit like arrays".
Now, it's true that we don't write "new array(1,2,3)"; but I have heard
people calling the "array()" syntax "the array constructor", and the
manual describes it as "creating an array". Similarly, you use
"constructor" and "construction" throughout the RFC.
All of that makes it feel perfectly natural for me to have "new Point(1,
5)" mean "create a Point record; feel free to save memory by reusing one
with the same values".
> A *record* may contain a traditional constructor with zero arguments
> to perform further initialization.
> A *record* body may also declare properties whose values are only
> mutable during a constructor call. At any other time, the property is
> immutable.
Talking of constructors, I find the proposed syntax rather confusing,
because it's doing the same job as constructor property promotion, but
in almost the opposite way: taking things out of the constructor
signature, vs putting them in:
readonly class Foo {
public string $bytes;
public function __construct(public int $len) {
$this->bytes = random_bytes($this->len);
}
}
record Foo (int $len) {
public string $bytes;
public function __construct() {
$this->bytes = random_bytes($this->len);
}
}
While writing this example, I realised that the behaviour is also
confusing: when exactly will the constructor be called? Consider:
$a = &Foo(42); // new Record; constructor called
$b = &Foo(42); // re-use cached Record; is the constructor skipped? or
called, but the result discarded? what if the constructor modifies
$this->len?
unset($a, $b);
$c = &Foo(42); // does this re-use the Record? or has it been garbage
collected, so this will call the constructor again?
I think we should decide between two paths:
- structs/records as a special kind of object, keeping as much behaviour
and syntax from classes as we can; that means no "inline constructor",
and probably no "pull a cached instance from memory"
- structs/records as a brand new thing, with new syntax that only allows
the parts that fit the model; that means no non-constructor properties,
and no constructor bodies
Regards,
--
Rowan Tommins
[IMSoP]
--------------rQWv1780KumNh803ioZG0gL4
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
Hi Rob,
I'm torn on this one. On the one hand,
it does look like a nice solution for adding custom value objects
to the language; on the other hand, it's a lot of things that are
"just slightly different" for users to get used to.
On 17/11/2024 21:30, Rob Landers wrote:
One
of the main reasons for the alternative creation syntax is because
I felt that "new" was misleading at best, and just plain wrong at
worst. It is also why I chose "&", to make it clear you are
not getting "a new one" but one that "just happens to exist with
the values you asked for." I'd be open to a different keyword or
something else entirely. It's just that "new" is the wrong one for
records.
I'm not convinced by this, because I'm not convinced the "mental
model" in the RFC is the one that most developers need to care
about. I think a much simpler mental model (for someone who
understands the rest of PHP) is:
- records are copy-on-write (like arrays, not like objects)
- the === operator returns true for two records with the same
value (again like arrays, not like objects)
- the implementation optimises two records with the same values to
share memory
The third point is useful to know if you're creating a lot of
them, but probably irrelevant most of the time.
We also might not want to make it a hard guarantee, because there
may be cases where a different trade-off is more efficient. For
instance, maybe we will optimise $foo->with(a: 1)->with(b:
2)->with(c: 3) to overwrite values in-place, at the cost of an
extra condition in the `===` implementation.
That would be similar to some of the changes to zvals in PHP 7;
for instance, "$foo=42; $bar=$foo;" will copy the value 42 to a
new piece of memory, not increase a zval reference count as PHP 5
would have done.
That leaves the mental model as mostly "records are a bit like
arrays". Now, it's true that we don't write "new array(1,2,3)";
but I have heard people calling the "array()" syntax "the array
constructor", and the manual describes it as "creating an array".
Similarly, you use "constructor" and "construction" throughout the
RFC.
All of that makes it feel perfectly natural for me to have "new
Point(1, 5)" mean "create a Point record; feel free to save memory
by reusing one with the same values".
A record may contain a
traditional constructor with zero arguments to perform further
initialization.
A record body may also
declare properties whose values are only mutable during a
constructor call. At any other time, the property is immutable.
Talking of constructors, I find the proposed syntax rather
confusing, because it's doing the same job as constructor property
promotion, but in almost the opposite way: taking things out of
the constructor signature, vs putting them in:
readonly class Foo {
public string $bytes;
public function __construct(public int $len) {
$this->bytes = random_bytes($this->len);
}
}
record Foo (int $len) {
public string $bytes;
public function __construct() {
$this->bytes = random_bytes($this->len);
}
}
While writing this example, I realised that the behaviour is also
confusing: when exactly will the constructor be called? Consider:
$a = &Foo(42); // new Record; constructor called
$b = &Foo(42); // re-use cached Record; is the constructor
skipped? or called, but the result discarded? what if the
constructor modifies $this->len?
unset($a, $b);
$c = &Foo(42); // does this re-use the Record? or has it been
garbage collected, so this will call the constructor again?
I think we should decide between two paths:
- structs/records as a special kind of object, keeping as much
behaviour and syntax from classes as we can; that means no "inline
constructor", and probably no "pull a cached instance from memory"
- structs/records as a brand new thing, with new syntax that only
allows the parts that fit the model; that means no non-constructor
properties, and no constructor bodies
Regards,
--
Rowan Tommins
[IMSoP]
--------------rQWv1780KumNh803ioZG0gL4--