Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123521 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 BB3D91A009C for ; Wed, 5 Jun 2024 16:57:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1717606697; bh=eCANQNVnDu1rQ5dceN8VIMblho90tmtnUFwPiCfcv50=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=ThNOh1jt9Nx6lzEfutKj1bBo1QIFZZ4I9ove1qUejp3PGPAqNJnUpRTNJqc1H0uEJ CCqbj7+tXCh6qj/vjzaxCFuhmaDINDZUoX+N9akAB4NNF0Fduqv+1tLKaUdumm0sSX nXuKiDywNGA0stC704hvnbjTIGen1JROaxSCdI8vTBtlh4hV4+ceYE+Ez6SIKhPr57 I5TnhMPfQ3AnvVWk90TVYwAQHMcnOJShwWDqGAh33KJs/U0XAHnBcImfebFwtY0Jrt XzPqY7UDVToSIK6vVkyhOMiFUyZLdh+tJRaoPK2gc53We6TQg04WcXxHtfv7nKLkfi PLr+QwlxGhuwQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id BB91F180057 for ; Wed, 5 Jun 2024 16:58:15 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail-lf1-f49.google.com (mail-lf1-f49.google.com [209.85.167.49]) (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 ; Wed, 5 Jun 2024 16:58:15 +0000 (UTC) Received: by mail-lf1-f49.google.com with SMTP id 2adb3069b0e04-52b98fb5c32so105277e87.1 for ; Wed, 05 Jun 2024 09:57:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1717606629; x=1718211429; darn=lists.php.net; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=AHzOXOhOCU3GKx/zz5tLBD09uX75X9sw+SmZ+YoyrLQ=; b=N3BIPv1m1+2d7s3awWZbiYa1q77hLePQbaiE2YmL99PozMC63lu8UX4mf251/54Gp8 lK1QGQ5FzBV5jhrbcTqJq7mldDrRIhZMa8KRC6EzYPYS9GRzDNHIRjd4Yo6LyF4kUwNZ oFcfKML7EVo0V8rJuEjdjPcucvE+wWseorMzogQOBdMOvF1sVhFNDlXwIz9Yv4l4Ie34 YhjM4QreGLAu+3lrlhmPaRRay14ywkqr1aqnUxqpbGwe+2j8qO6MfB5SUX4S4PfMqRky AkZkyKatqpHGE+opHOQwpDxadQ7cIaJqWb40akslDoYLvCbE7jYVB269Rrxpx8TSZkWv MJfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717606629; x=1718211429; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=AHzOXOhOCU3GKx/zz5tLBD09uX75X9sw+SmZ+YoyrLQ=; b=LuTgp3BUg9fJ0qb8jRoTLMxKrbBv3dx5wu9YB4ESBgd/bpmHOUUMQ9sg4JWhTIHtGu ynV2x7RTRiyQaP0zAviL1bT4trcAJzTpwwuAj8GPjHeMrNyhLiUtEaLSY5DWn6P7Gjg/ JOteN1JS20p0Q5a7fUbJBqV7X65gc5FoHWuQEbmTYwMnFWV8o8EujOB7Q6KFuOXCi3bC WbyEU0TlMtu4Sry3viA9L5b1wSYPmxL5M/CKJH1ZrSxy87CFofaisgsuw7Rb1TQmV1dx ihjOCft0QfWiZFgSUue2EIsalPplKQNGFvFEe2GpEwJ4q8xbjTuWtp6ha3hJD7348x3l 3AIA== X-Gm-Message-State: AOJu0YwSeyILH6BvWM7DpA2oSbgWFTZEIiNQR9ENlsRN3gpurQ6wU4vg EkIhiNcAnHJRKBXJquW1idTan9ijvnI9X4AT/yEnS2vNeLOjXqHmiQbIWA== X-Google-Smtp-Source: AGHT+IFDa7KxCvI5THJu9//7X7i9BV4bVepUXdG8Y6EcLJQHTWE8/zz9PMx4J4B/zak6S7uNGVuF6w== X-Received: by 2002:a05:6512:31ca:b0:518:9ce1:a5bb with SMTP id 2adb3069b0e04-52bab4fcbfbmr3080767e87.54.1717606628634; Wed, 05 Jun 2024 09:57:08 -0700 (PDT) Received: from smtpclient.apple ([89.249.45.14]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a68b7b8c33asm664562166b.69.2024.06.05.09.57.07 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 05 Jun 2024 09:57:08 -0700 (PDT) Message-ID: <10AAAA51-9538-4F97-83D1-91F154C745F7@gmail.com> Content-Type: multipart/alternative; boundary="Apple-Mail=_65A2C20C-881C-4BE5-A0B1-B1FF463D2536" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3774.600.62\)) Subject: Re: [PHP-DEV] [RFC] Asymmetric Visibility, v2 Date: Wed, 5 Jun 2024 18:56:57 +0200 In-Reply-To: <6d644f0f-cbeb-484c-b267-bf1d97e6d27a@app.fastmail.com> Cc: php internals To: Larry Garfield References: <0a6a61cd-f203-4dea-a7f8-97e6b885c52d@app.fastmail.com> <734bb8e8-2fdf-4e50-9039-e53c99ee4930@app.fastmail.com> <89b695d7-661f-4c1b-ac4c-4480ad158e83@app.fastmail.com> <6d644f0f-cbeb-484c-b267-bf1d97e6d27a@app.fastmail.com> X-Mailer: Apple Mail (2.3774.600.62) From: claude.pache@gmail.com (Claude Pache) --Apple-Mail=_65A2C20C-881C-4BE5-A0B1-B1FF463D2536 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > Le 5 juin 2024 =C3=A0 16:28, Larry Garfield a = =C3=A9crit : >=20 > On Fri, May 31, 2024, at 8:59 PM, Larry Garfield wrote: >> On Fri, May 31, 2024, at 5:45 PM, Claude Pache wrote: >>>> Le 31 mai 2024 =C3=A0 18:08, Larry Garfield = a =C3=A9crit : >>>>=20 >>>> However, this also brings up another interesting issue: readonly = properties (in 8.3) DO allow redeclaration, essentially adjusting the = property scope (the class that declares it) to make the visibility check = pass. That is, the definition of the class it is private to changes, = which is different from how inheritance works elsewhere. When the = parent writes to the same property, a special check is needed to verify = the two properties are related. All that special casing effectively = means that readonly in 8.4 wouldn't really be "write once + = private(set)", but "write once + private(set) - final", which is... just = kinda screwy. That means our options are: >>>>=20 >>>> * A BC break on readonly (not allowing it to be overridden) >>>> * Make readonly an exception to the implicit final. >>>> * Just don't allow readonly with aviz after all. >>>=20 >>> Another possible option is: >>>=20 >>> * Make readonly be `protected(set)` by default. >>>=20 >>> That would weaken the originally intended semantics of readonly, but = in=20 >>> a compatible and acceptable way? >>>=20 >>> =E2=80=94Claude >>=20 >> Only sort of compatible. It would allow readonly props to be=20 >> redefined, and wouldn't break any existing code, I think... but it=20 >> would mean any time you use readonly, suddenly a child class can come=20= >> along and mess with it out from under you. >>=20 >> In practice that's likely not a common concern, but it is a behavior=20= >> change. I think it's possible (I need to confirm with Ilija), if we=20= >> want that slight BC break, but I don't know if we do. >>=20 >> --Larry Garfield >=20 > Ilija and I have been discussing this issue over the last few days. = We agree that `private(set)` should imply `final`, as that eliminates a = bunch of issues both implementation-wise and expectation-wise. However, = that causes an issue for `readonly`. =20 >=20 > The root issue is that if we say "`readonly int $x` is really just = `private(set) readonly int $x`", that runs into the issue of "whelp, = you've just made readonly always final, which is a BC break." So that's = no good. >=20 > We see a couple of ways to resolve this, presented below in our order = of preference. >=20 > 1. Disallow readonly with aviz =3D> No BC break, and we don't need to = define readonly in terms of private(set). The only really useful = combination anyway would be `readonly protected(set)`, in which case the = protected(set) is doing 90% of the work already. There's few cases = where the readonly is truly necessary at that point. Any other oddball = edge cases could be handled by a custom hook. > 2. Make `readonly` implicitly `protected(set)` unless you explicitly = specify `private(set)` =3D> Would have the most consistent result, and = this is probably the cleanest in the engine, as `readonly private(set)` = would mean exactly what it says on the tin, with no inconsistency of = "well it's kinda sorta `private(set)`" as `readonly` has now. However, = this would be an expectation change, as suddenly all readonly properties = could be written to from a child class. That may be good in some cases = but it's possible some objects could have unexpected behavior if they = didn't expect to be extended. (No existing code will break, but you = could now do things to it in a child class that the author didn't = anticipate.) > 3. You can't mix `readonly` with `private(set)`, but can use other = visibilities =3D> No BC break, and we don't need to define readonly in = terms of `private(set)`. However, it means the implicit `private(set)` = of `readonly` and an explicit private(set) behave differently (one is = final, one is not). It also unclear if a `readonly` property can be = overridden with `readonly protected(set)` only, or also `readonly = private(set)`. If the latter, does it become implicitly `final` at that = point? > 4. `readonly` behaves differently for an explicit (final) and implicit = (not-final) `private(set)` =3D> No BC break, but it's kinda weird and = non-obvious to explain. It also has the same non-obvious inheritance = questions as option 3. >=20 > We consider only the first two to be really viable. For simplicity, = we'd favor doing option 1, and if desired option 2 could be presented in = the future as its own RFC as that is technically a behavior change, not = just addition, so deserves careful consideration. However, if there is = a clear consensus to go with option 2 now, we're open to that. >=20 > --Larry Garfield Hi Larry and Ilija, Thanks for your work. Here is my opinion: First, I do think that `readonly` should integrate with aviz, unless = that implies truly controversial changes on `readonly`. As Theodore = Brown commented in the previous version of the RFC: =E2=80=9CProposal = feels unfinished since it can't be used in conjunction with readonly = properties/classes. In my opinion the issues with this need to be = resolved first, to avoid the language moving towards a messy hodgepodge = of features that don't work well together.=E2=80=9D Second, I think that making `readonly` implicitly `protected(set)` by = default (Option 2) is the way to go: * At first glance it is an expectation change. But, in reality, all = readonly properties can *already* be written to from a child class as of = today: it suffices that the child class in question redeclare those = properties: https://3v4l.org/9AV4r. =46rom the point of view of the = child class, the only thing that will change, is that it will no longer = be required to explicitly opt into that possibility by redeclaring the = readonly properties. =46rom the point of view of the parent class, = nothing will change, except false expectations=E2=80=94and it is a good = thing that false expectations are eliminated. * Relatively of Options 3 and 4, Option 2 leaves the language in a more = simple and regular state. =E2=80=94Claude --Apple-Mail=_65A2C20C-881C-4BE5-A0B1-B1FF463D2536 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

Le 5 juin 2024 =C3=A0 16:28, Larry Garfield = <larry@garfieldtech.com> a =C3=A9crit :

On Fri, May 31, 2024, at = 8:59 PM, Larry Garfield wrote:
On Fri, May = 31, 2024, at 5:45 PM, Claude Pache wrote:
Le 31 mai 2024 =C3=A0 18:08, = Larry Garfield <larry@garfieldtech.com> a =C3=A9crit = :

However, this also brings up another interesting issue: = readonly properties (in 8.3) DO allow redeclaration, essentially = adjusting the property scope (the class that declares it) to make the = visibility check pass. That is, the definition of the class it is = private to changes, which is different from how inheritance works = elsewhere.  When the parent writes to the same property, a special = check is needed to verify the two properties are related.  All that = special casing effectively means that readonly in 8.4 wouldn't really be = "write once + private(set)", but "write once + private(set) - final", = which is... just kinda screwy.  That means our options = are:

* A BC break on readonly (not allowing it to be = overridden)
* Make readonly an exception to the implicit final.
* = Just don't allow readonly with aviz after = all.

Another possible option is:

* Make = readonly be `protected(set)` by default.

That would weaken the = originally intended semantics of readonly, but in
a compatible and = acceptable way?

=E2=80=94Claude

Only sort of = compatible.  It would allow readonly props to be
redefined, and = wouldn't break any existing code, I think... but it
would mean any = time you use readonly, suddenly a child class can come
along and = mess with it out from under you.

In practice that's likely not a = common concern, but it is a behavior
change.  I think it's = possible (I need to confirm with Ilija), if we
want that slight BC = break, but I don't know if we do.

--Larry = Garfield

Ilija and I have been discussing this issue = over the last few days.  We agree that `private(set)` should imply = `final`, as that eliminates a bunch of issues both implementation-wise = and expectation-wise.  However, that causes an issue for = `readonly`.  

The root issue is that if we say "`readonly = int $x` is really just `private(set) readonly int $x`", that runs into = the issue of "whelp, you've just made readonly always final, which is a = BC break."  So that's no good.

We see a couple of ways to = resolve this, presented below in our order of preference.

1. = Disallow readonly with aviz =3D> No BC break, and we don't need to = define readonly in terms of private(set).  The only really useful = combination anyway would be `readonly protected(set)`, in which case the = protected(set) is doing 90% of the work already.  There's few cases = where the readonly is truly necessary at that point.  Any other = oddball edge cases could be handled by a custom hook.
2. Make = `readonly` implicitly `protected(set)` unless you explicitly specify = `private(set)` =3D> Would have the most consistent result, and this = is probably the cleanest in the engine, as `readonly private(set)` would = mean exactly what it says on the tin, with no inconsistency of "well = it's kinda sorta `private(set)`" as `readonly` has now.  However, = this would be an expectation change, as suddenly all readonly properties = could be written to from a child class.  That may be good in some = cases but it's possible some objects could have unexpected behavior if = they didn't expect to be extended.  (No existing code will break, = but you could now do things to it in a child class that the author = didn't anticipate.)
3. You can't mix `readonly` with `private(set)`, = but can use other visibilities =3D> No BC break, and we don't need to = define readonly in terms of `private(set)`.  However, it means the = implicit `private(set)` of `readonly` and an explicit private(set) = behave differently (one is final, one is not).  It also unclear if = a `readonly` property can be overridden with `readonly protected(set)` = only, or also `readonly private(set)`.  If the latter, does it = become implicitly `final` at that point?
4. `readonly` behaves = differently for an explicit (final) and implicit (not-final) = `private(set)` =3D> No BC break, but it's kinda weird and non-obvious = to explain.  It also has the same non-obvious inheritance questions = as option 3.

We consider only the first two to be really viable. =  For simplicity, we'd favor doing option 1, and if desired option 2 = could be presented in the future as its own RFC as that is technically a = behavior change, not just addition, so deserves careful consideration. =  However, if there is a clear consensus to go with option 2 now, = we're open to that.

--Larry = Garfield

Hi Larry and = Ilija,

Thanks for your work. Here is my = opinion:

First, I do think that `readonly` = should integrate with aviz, unless that implies truly controversial = changes on `readonly`. As Theodore Brown commented in the previous = version of the RFC: =E2=80=9CProposal feels unfinished since it can't be = used in conjunction with readonly properties/classes. In my opinion the = issues with this need to be resolved first, to avoid the language moving = towards a messy hodgepodge of features that don't work well = together.=E2=80=9D

Second, I think that making = `readonly` implicitly `protected(set)` by default (Option 2) is the way = to go:
* At first glance it is an expectation change. But, in = reality, all readonly properties can *already* be written to from a = child class as of today: it suffices that the child class in question = redeclare those properties: https://3v4l.org/9AV4r. =46rom the = point of view of the child class, the only thing that will change, is = that it will no longer be required to explicitly opt into that = possibility by redeclaring the readonly properties. =46rom the point of = view of the parent class, nothing will change, except false = expectations=E2=80=94and it is a good thing that false expectations are = eliminated.
* Relatively of Options 3 and 4, Option 2 leaves = the language in a more simple and regular = state.

=E2=80=94Claude

<= br>
= --Apple-Mail=_65A2C20C-881C-4BE5-A0B1-B1FF463D2536--