# [RFC] Asymmetric Visibility, v2

- Thread: https://externals.io/message/123456
- Original archive: https://news-web.php.net/php.internals/123456
- Started by Larry Garfield on 2024-05-29T19:15:45+00:00
- Messages: 57
- Page 1 of 3: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/123456.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #123456 — Larry Garfield — 2024-05-29T19:15:45+00:00

URL: https://externals.io/message/123456#123456

As promised, Ilija and I offer this revised version of asymmetric visibility.  

https://wiki.php.net/rfc/asymmetric-visibility-v2

It's still essentially the same as last year's version, but with a few adjustments and changes:

* readonly properties are now supported in a logical fashion.
* We've brought back the abbreviated form, as public-read, something else set is the most common use case.
* The section on magic methods has been greatly simplified.  The implementation itself hasn't changed, but the explanation is a lot less confusing now.
* We've explained how aviz interacts with hooks (they don't, really) and with interface properties (in the obvious way), which didn't exist at the time of the last draft.
* We've added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
* We've added a section discussing why the prefix-style syntax was chosen.

*dons flame retardant suit*

-- 
  Larry Garfield
  larry@garfieldtech.com

---

## #123457 — tim@bastelstu.be — 2024-05-29T19:51:03+00:00

URL: https://externals.io/message/123456#123457 · In reply to: #123456

Hi


> * We've brought back the abbreviated form, as public-read, something else set is the most common use case.

The most common use case is that 'get' and 'set' are symmetric. Any 
divergence from that should stand out and I think that the hamming 
distance between

     protected string $foo;

and

     protected(set) string $foo;

is too small.

Other than that the proposal looks good to me. Ship it.

------

One note regarding the text. You already confirmed to me in private that:

     class Foo {
         private $dontTouchMe;
     }

     $backdoor = function ($key, $value) { $this->{$key} = $value; };

     $f = new Foo();
     $backdoor->call($f, 'dontTouchMe', 'butIDid');
     var_dump($f);

would work as expected with aviz. It would make sense to explicitly 
spell that out, just like it's explicitly spelled out that 
`ReflectionProperty::setValue()` works.

Best regards
Tim Düsterhus

---

## #123458 — Andreas Hennings — 2024-05-29T19:53:41+00:00

URL: https://externals.io/message/123456#123458 · In reply to: #123456

Hello Larry,
just a quick thought.
Is there a reason why we cannot just make it "public private string
$x" instead of "public private(set) string $x"?
We would define that the second visibility specifier is for write.

The current proposal with "public private(set)" is less ambiguous, and
it is immediately obvious that this is something new, and not just
somebody accidentally added two modifiers.
At the same time, it feels a bit alien and cluttered.

Other options could be something like "public:private" or "public-private".

A consequence of such options would be that you always need to specify
the read visibility along with the write visibility.
But this seems ok to me.

This is not a final opinion, just a thought.

-- Andreas


>
> As promised, Ilija and I offer this revised version of asymmetric visibility.
>
> https://wiki.php.net/rfc/asymmetric-visibility-v2
>
> It's still essentially the same as last year's version, but with a few adjustments and changes:
>
> * readonly properties are now supported in a logical fashion.
> * We've brought back the abbreviated form, as public-read, something else set is the most common use case.
> * The section on magic methods has been greatly simplified.  The implementation itself hasn't changed, but the explanation is a lot less confusing now.
> * We've explained how aviz interacts with hooks (they don't, really) and with interface properties (in the obvious way), which didn't exist at the time of the last draft.
> * We've added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
> * We've added a section discussing why the prefix-style syntax was chosen.
>
> *dons flame retardant suit*
>
> --
>   Larry Garfield
>   larry@garfieldtech.com

---

## #123459 — Larry Garfield — 2024-05-29T20:02:54+00:00

URL: https://externals.io/message/123456#123459 · In reply to: #123458

> Hello Larry,
> just a quick thought.
> Is there a reason why we cannot just make it "public private string
> $x" instead of "public private(set) string $x"?
> We would define that the second visibility specifier is for write.
>
> The current proposal with "public private(set)" is less ambiguous, and
> it is immediately obvious that this is something new, and not just
> somebody accidentally added two modifiers.
> At the same time, it feels a bit alien and cluttered.
>
> Other options could be something like "public:private" or "public-private".
>
> A consequence of such options would be that you always need to specify
> the read visibility along with the write visibility.
> But this seems ok to me.
>
> This is not a final opinion, just a thought.
>
> -- Andreas

We went through a bunch of syntax variations last year, including "public private", "public:private", and "public private:set", plus a few others.  In an RCV poll, public private(set) was the favorite. (See link at the end of the RFC.)  It also allows for extension to other operations and scopes, and for the short-hand syntax.  Many of the other options did not support those.  Thus we stuck with the known syntax that had the most flexibility and most support.

By other operations, I mean, suppose we allow varying the visibility for obtaining a reference separate from a set (for some reason).  It's obvious how that would look with the current syntax: public protected(ref) private(set).  With "public private" or "public:private", it's really not clear how we'd even do that.

--Larry Garfield

---

## #123460 — tim@bastelstu.be — 2024-05-29T20:09:00+00:00

URL: https://externals.io/message/123456#123460 · In reply to: #123458

Hi


> Is there a reason why we cannot just make it "public private string
> $x" instead of "public private(set) string $x"?
> We would define that the second visibility specifier is for write.
> 
> The current proposal with "public private(set)" is less ambiguous, and
> it is immediately obvious that this is something new, and not just
> somebody accidentally added two modifiers.
> At the same time, it feels a bit alien and cluttered.
> 
> Other options could be something like "public:private" or "public-private".

The variant with the parentheses is the most future-proof one, should 
additional operations be added. It would allow for:

     public private(set,unset,frobnicate) string $foo;

Even if tools would not yet understand whatever 'frobnicate' does, they 
would know that this is an operation that may be performed on $foo and 
would be able to syntax highlight the visibility definition properly and 
they would also know how to autoformat it, without needing to add 
support for the new keyword.

Best regards
Tim Düsterhus

---

## #123463 — Larry Garfield — 2024-05-29T21:12:25+00:00

URL: https://externals.io/message/123456#123463 · In reply to: #123457

> Hi
>
> 
> > […]
>
> The most common use case is that 'get' and 'set' are symmetric. 

OK, fair, I meant in the most common case where you're using aviz at all, get is probably public.

That said, with both hooks and aviz, I can see data objects, for instance, becoming commonly public-get, private-set.  It's already common to have them be public readonly, so this is just an extension of that.

> Any 
> divergence from that should stand out and I think that the hamming 
> distance between
>
>      protected string $foo;
>
> and
>
>      protected(set) string $foo;
>
> is too small.

I can only respectfully disagree here.  I think it's reasonably self-evident, made moreso by the (), which as Andreas noted looks weird when you're not used to it (but we should get used to fairly quickly).  And the benefit of not having to type "public" on every property outweighs any initial confusion, much the same as readonly classes just reduces boilerplate.

> One note regarding the text. You already confirmed to me in private that:
>
>      class Foo {
>          private $dontTouchMe;
>      }
>
>      $backdoor = function ($key, $value) { $this->{$key} = $value; };
>
>      $f = new Foo();
>      $backdoor->call($f, 'dontTouchMe', 'butIDid');
>      var_dump($f);
>
> would work as expected with aviz. It would make sense to explicitly 
> spell that out, just like it's explicitly spelled out that 
> `ReflectionProperty::setValue()` works.

Added a note to that effect in the Reflection section as well.  Thanks.

--Larry Garfield

---

## #123464 — drealecs@gmail.com — 2024-05-30T08:35:42+00:00

URL: https://externals.io/message/123456#123464 · In reply to: #123456

On Wed, May 29, 2024 at 10:18 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> As promised, Ilija and I offer this revised version of asymmetric
> visibility.
>
> https://wiki.php.net/rfc/asymmetric-visibility-v2
>
>
Hey Larry, Ilija,

I have one concern so far, and it's related to the inheritance section.

If in a class I define the property as private,
I know that there is no way for external scope or extending classes scope
to read or write to the property.
(of course, ignoring reading/writing using reflection or re-binded closures)

If an extending class defines the property with a wider visibility,
protected or public, it will shadow the initial one and not change its
visibility.

Now, if I define the property as public private(set) (or protected
private(set)) with similar intentions,
to make sure that there is no way for external scope or extending classes
scope to write to the property,
while allowing reading from external scope (or extending classes scope).

But the problem is that an extending class can define the property as
public protected(set) (or protected protected(set)),
and that will easily allow the property that I wanted to make sure it is
private for writing to be changed by an extending class to be protected.


The main suggestion I can think of, is to now allow widening the write
visibility when it is private.

I mean, in general, to make sure it is not possible for other extending
classes to access private parent class properties/methods we can use two
mechanisms:
- disallowing to change the visibility and making it an error
- shadowing the parent property/method
While shadowing works for symmetric visibility, it doesn't really work for
asymmetric visibility, so disallowing seems to be a good option here.

Also, maybe marking the properties as final could play a role here,
not allowing private visibility for write on properties to be widened or
completely not allowing it to change/redefined.


Regards,
Alex

---

## #123465 — Vincent de Lau — 2024-05-30T10:16:57+00:00

URL: https://externals.io/message/123456#123465 · In reply to: #123459

> -----Original Message-----
> From: Larry Garfield <larry@garfieldtech.com>
> Sent: Wednesday, May 29, 2024 10:03 PM
> 
> 
> > […]
> private".
> > […]

> We went through a bunch of syntax variations last year, including "public
> private", "public:private", and "public private:set", plus a few others.
> In an RCV poll, public private(set) was the favorite. (See link at the end
> of the RFC.)  It also allows for extension to other operations and scopes,
> and for the short-hand syntax.  Many of the other options did not support
> those.  Thus we stuck with the known syntax that had the most flexibility
> and most support.

Would it make sense to do another RCV poll now that hooks are accepted, after lengthy discussion over its syntax? 

Personally, I would prefer the HookEmbeddedStyle form. I don't think the argument that the visibility would potentially be separated from the hook definition is very strong, as you would have to scan for the existence of the hook anyway. For me, asymmetric visibility and property hooks are mentally more related than the RFC technically defines them to be.

Furthermore, I see a lot of reasoning in favour of the prefix syntax in relation to limitations of other language constructs like property promotion. While I don't think it is fair to expect this RFC should fix those, to me it feels we are compounding 'errors'.

Thanks to both you and Ilija for all the hard work on these RFC's, it is much appreciated!

--
Vincent de Lau

---

## #123466 — Derick Rethans — 2024-05-30T15:07:17+00:00

URL: https://externals.io/message/123456#123466 · In reply to: #123464

> On Wed, May 29, 2024 at 10:18 PM Larry Garfield <larry@garfieldtech.com>
> wrote:
> 
> > […]
> Hey Larry, Ilija,
> 
> I have one concern so far, and it's related to the inheritance section.
> 
> If in a class I define the property as private,
> I know that there is no way for external scope or extending classes scope
> to read or write to the property.
> (of course, ignoring reading/writing using reflection or re-binded closures)
> 
> If an extending class defines the property with a wider visibility,
> protected or public, it will shadow the initial one and not change its
> visibility.

private and protected differ here already, even without async 
visibility:

https://3v4l.org/8Ynog

A protected property does not create a new bag to store data in, which 
does happen for a private property.

> Now, if I define the property as public private(set) with similar 
> intentions, to make sure that there is no way for external scope or 
> extending classes scope to write to the property, while allowing 
> reading from external scope (or extending classes scope).
> 
> But the problem is that an extending class can define the property as 
> public protected(set), and that will easily allow the property that I 
> wanted to make sure it is private for writing to be changed by an 
> extending class to be protected.

public private(set) properties aren't really private, so you don't get 
the shadowing, but you do have a point wrt to the expectation that an 
inherited class can't easily override the private(set) part (with 
protected(set) or public(set)).

Hopefully Ilija or Larry can explain :-)

cheers,
Derick

---

## #123468 — Claude Pache — 2024-05-31T07:29:48+00:00

URL: https://externals.io/message/123456#123468 · In reply to: #123466

> Le 30 mai 2024 à 17:07, Derick Rethans <derick@php.net> a écrit :
> 
> > […]
> 
> public private(set) properties aren't really private, so you don't get 
> the shadowing, but you do have a point wrt to the expectation that an 
> inherited class can't easily override the private(set) part (with 
> protected(set) or public(set)).



Note that the issue already exists today with readonly properties: those are basically private(set); but if you redeclare a non-private readonly property in a subclass, you can in fact initialise it from the subclass bypassing the initial private(set) restriction of the superclass: https://3v4l.org/9AV4r

If you want a property not to be overridable, end of story, you can mark it as `final` (the final marker for properties was added as part of the hooks RFC, but it works also with non-hooked properties).

—Claude

---

## #123469 — drealecs@gmail.com — 2024-05-31T12:04:40+00:00

URL: https://externals.io/message/123456#123469 · In reply to: #123468

On Fri, May 31, 2024 at 10:30 AM Claude Pache <claude.pache@gmail.com>
wrote:

>
>
> Le 30 mai 2024 à 17:07, Derick Rethans <derick@php.net> a écrit :
>
>
> Now, if I define the property as public private(set) with similar
> intentions, to make sure that there is no way for external scope or
> extending classes scope to write to the property, while allowing
> reading from external scope (or extending classes scope).
>
> But the problem is that an extending class can define the property as
> public protected(set), and that will easily allow the property that I
> wanted to make sure it is private for writing to be changed by an
> extending class to be protected.
>
>
> public private(set) properties aren't really private, so you don't get
> the shadowing, but you do have a point wrt to the expectation that an
> inherited class can't easily override the private(set) part (with
> protected(set) or public(set)).
>
>
>
> Note that the issue already exists today with readonly properties: those
> are basically private(set); but if you redeclare a non-private readonly
> property in a subclass, you can in fact initialise it from the subclass
> bypassing the initial private(set) restriction of the superclass:
> https://3v4l.org/9AV4r
>

Yes, thank you; that's a good point.
Seems like another issue with readonly, but then again, the private(set)
part of readonly is not really explicitly designed, I guess.


>
> If you want a property not to be overridable, end of story, you can mark
> it as `final` (the final marker for properties was added as part of the
> hooks RFC, but it works also with non-hooked properties).
>

Yes, it seems like a good enough option, and use "final public
private(set)" to ensure only the current class will be able to set the
value.
If we all agree, I think this should be documented in the RFC.

There is another small problem, I think, with "final" modifier not being
allowed for constructor-promoted properties.
And maybe we can have this fixed in the current RFC if this ends up
being "the correct" way to define public-read private-write properties.

Regards,
Alex

---

## #123470 — Claude Pache — 2024-05-31T13:11:46+00:00

URL: https://externals.io/message/123456#123470 · In reply to: #123465

> Le 30 mai 2024 à 12:16, Vincent de Lau <vincent@delau.nl> a écrit :
> 
> > […]
> 
> Would it make sense to do another RCV poll now that hooks are accepted, after lengthy discussion over its syntax? 
> 

At the time the poll was conducted, it was already known that a hooks RFC was in preparation, that could be compatible with either option, syntax-wise. Now, we have hooks that are compatible with both options, syntax-wise. I don’t think that would change the aesthetic preferences of people.

But now, we have indeed more information, namely detailed technical information on how the two features (hooks and aviz) interact effectively with references/arrays/readonly. At the time the poll was conducted, I was *moderately* in favour of the Swift-style syntax, mostly based on the general principle that things that are logically orthogonal should be implemented as orthogonal. If the same poll is done today, I will be *strongly* in favour of the Swift-style syntax, because I know more precisely how both features interact with arrays and readonly.

—Claude

---

## #123475 — Larry Garfield — 2024-05-31T16:08:53+00:00

URL: https://externals.io/message/123456#123475 · In reply to: #123469

> 
> > […]
>
> Yes, thank you; that's a good point.
> Seems like another issue with readonly, but then again, the 
> private(set) part of readonly is not really explicitly designed, I 
> guess.
> 
> > […]
>
> Yes, it seems like a good enough option, and use "final public 
> private(set)" to ensure only the current class will be able to set the 
> value.
> If we all agree, I think this should be documented in the RFC.
>
> There is another small problem, I think, with "final" modifier not 
> being allowed for constructor-promoted properties.
> And maybe we can have this fixed in the current RFC if this ends up 
> being "the correct" way to define public-read private-write properties.
>
> Regards,
> Alex

Mm, yeah, this is an interesting corner case we'd not thought of.  We spent a little time looking at how other languages handle this.

Kotlin just disallows using private-set on a non-final property.  (Kotlin properties are final by default, unless otherwise specified.)  cf: https://kotlinlang.org/spec/inheritance.html#overriding

C# disallows changing property visibility in child classes entirely.  cf: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility#access-modifiers-on-overriding-accessors

Swift is... weird.  If you have a public private(set) property, you can override it by turning it into a virtual property with only a get hook.  If you also define a set hook, though, it just never gets called.  It doesn't appear that you can widen the set visibility, I think.

So since we now have final properties (yay, hooks RFC!), we could probably mostly solve this by just only allowing private(set) on a final property, like Kotlin.  That's probably the easiest approach.  We can also make private(set) properties implicitly final to avoid lots of boilerplate.  (Having to always type final with private(set) every time is kinda silly.)

It would mean that proxy objects implemented using child classes with hooks would only work with a public protected(set) class, not with a private(set), but that's probably fine, and since no one is doing that quite yet, obviously, there's no existing code to be concerned about.

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.

After some consideration, we believe the third option is best (least bad).  The other options add still-more special cases or break existing code, neither of which are good.  If a property is declared private(set), then child classes simply should not be allowed to change that, period.  Even adding a get hook in a child is potentially a problem, as it would change the "round trip" behavior in ways the parent class doesn't expect.  Disallowing readonly and making private(set) implicitly final avoids the loopholes in the language that would violate that expectation.

With aviz, all the use cases for readonly are essentially covered already.  If you have private(set), you can control when it gets written anyway, so if you want it to be write-once, it’s write once.  If you have protected(set), you’re allowing a child to set it, so they can do so whenever they want anyway, and could be doing it out of order from the parent class to begin with.

So if you wanted the equivalent of "public protected(set) readonly string $foo", in practice, the readonly is not giving you much to begin with.  And "public public(set) readonly string $foo"... we just can't really think of a good use case for.  Even if one exists, that could be emulated with a set hook now.

In terms of code length, thanks to the optional public-get visibility, the amount of typing is roughly the same:

readonly class ReadonlyPoint {
    public function __construct(
        public int $x,
        public int $y,
    ) {}
}

class AvizPoint {
    public function __construct(
        private(set) int $x,
        private(set) int $y,
    ) {}
}

(Or protected, if you prefer.)  The latter provides a lot more flexibility, at the cost of "enforce write-once yourself in the class if you care", which seems an entirely reasonable tradeoff.

So we feel the best way forward is to make the following changes:

* private(set) implicitly means "final".  (You can declare it explicitly if you want, but it isn't necessary.)
* Make readonly incompatible with aviz again.

Thoughts?

Also, Alexandru noted earlier that final properties don't seem to be supported in constructor promotion.  That's an oversight from the hooks implementation, not a design choice, so Ilija will just fix that as part of the hooks PR before it gets fully merged.

--Larry Garfield

---

## #123479 — Claude Pache — 2024-05-31T17:45:55+00:00

URL: https://externals.io/message/123456#123479 · In reply to: #123475

> Le 31 mai 2024 à 18:08, Larry Garfield <larry@garfieldtech.com> a écrit :
> 
> 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?

—Claude

---

## #123480 — Larry Garfield — 2024-05-31T20:59:51+00:00

URL: https://externals.io/message/123456#123480 · In reply to: #123479

> > […]
>
> 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?
>
> —Claude

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

---

## #123481 — Robert Landers — 2024-05-31T21:26:53+00:00

URL: https://externals.io/message/123456#123481 · In reply to: #123479

>
>
>
> Le 31 mai 2024 à 18:08, Larry Garfield <larry@garfieldtech.com> a écrit :
>
> 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?
>
> —Claude
>
>

I know this doesn't really contribute to the conversation ... but if I
could ever mash a +1 on a single email, this is the email I'd choose.

"Best elegant solution that happens to delete readonly without
deleting readonly" award.

Robert Landers
Software Engineer
Utrecht NL

---

## #123482 — Claude Pache — 2024-05-31T22:21:01+00:00

URL: https://externals.io/message/123456#123482 · In reply to: #123475

> Le 31 mai 2024 à 18:08, Larry Garfield <larry@garfieldtech.com> a écrit :
> 
> So we feel the best way forward is to make the following changes:
> 
> * private(set) implicitly means "final".  (You can declare it explicitly if you want, but it isn't necessary.)
> * Make readonly incompatible with aviz again.
> 
> Thoughts?


After reflection, I don’t think that we need to make readonly incompatible with aviz, even with the current semantics of readonly (at least logically; no idea about implementationally):

* legacy-readonly properties could keep their own peculiar `private-overridable(set)` if they want;
* aviz-readonly properties have, by definition, one of `public(set)`, `protected(set)` or `private(set)` marker; those will work regularly, including the implicit `final` attached to `private(set)`;
* a non-aviz readonly property could not be redeclared in a subclass as aviz-readonly, and vice versa.

—Claude

---

## #123483 — Erick de Azevedo Lima — 2024-06-01T01:40:34+00:00

URL: https://externals.io/message/123456#123483 · In reply to: #123482

> So we feel the best way forward is to make the following changes:
>
> * private(set) implicitly means "final".  (You can declare it explicitly
if you want, but it isn't necessary.)
> * Make readonly incompatible with aviz again.

I'd make readonly incompatible with aviz. Readonly props have its
"peculiarities" that are being (found and) changed on each version.
If you don't want to change your value ever, use readonly.
If you want more flexibility, use aviz.

Making workarounds on the aviz RFC so it can be compatible with readonly is
not worth it, IMO.

Best regards,
Erick

Em sex., 31 de mai. de 2024 às 20:53, Claude Pache <claude.pache@gmail.com>
escreveu:

>
>
> Le 31 mai 2024 à 18:08, Larry Garfield <larry@garfieldtech.com> a écrit :
>
> So we feel the best way forward is to make the following changes:
>
> * private(set) implicitly means "final".  (You can declare it explicitly
> if you want, but it isn't necessary.)
> * Make readonly incompatible with aviz again.
>
> Thoughts?
>
>
>
> After reflection, I don’t think that we need to make readonly incompatible
> with aviz, even with the current semantics of readonly (at least logically;
> no idea about implementationally):
>
> * legacy-readonly properties could keep their own peculiar
> `private-overridable(set)` if they want;
> * aviz-readonly properties have, by definition, one of `public(set)`,
> `protected(set)` or `private(set)` marker; those will work regularly,
> including the implicit `final` attached to `private(set)`;
> * a non-aviz readonly property could not be redeclared in a subclass as
> aviz-readonly, and vice versa.
>
> —Claude

---

## #123488 — drealecs@gmail.com — 2024-06-03T07:54:07+00:00

URL: https://externals.io/message/123456#123488 · In reply to: #123475

On Fri, May 31, 2024 at 7:13 PM Larry Garfield <larry@garfieldtech.com>
wrote:

>
> So we feel the best way forward is to make the following changes:
>
> * private(set) implicitly means "final".  (You can declare it explicitly
> if you want, but it isn't necessary.)
> * Make readonly incompatible with aviz again.
>
> Thoughts?
>

I think making properties `final` when using `private(set)` is a good
solution to this.

I don't think `readonly` needs to be incompatible with aviz. We can have
`readonly` act as `private(set`) but not `final` by default.
But you can define it as `private(set) readonly`, and in this case it will
be `final`, practically the same as `final readonly`.
It would go like this:
- private(set) -> final, private(set)
- final private(set) -> final, private(set)
- readonly -> write-once, private(set)
- final readonly -> final, write-once, private(set)
- private(set) readonly -> final, write-once, private(set)
- protected(set) readonly -> write-once, protected(set)
- public(set) readonly -> write-once, public(set)


>
> Also, Alexandru noted earlier that final properties don't seem to be
> supported in constructor promotion.  That's an oversight from the hooks
> implementation, not a design choice, so Ilija will just fix that as part of
> the hooks PR before it gets fully merged.
>

Maybe we need to have some discussion/considerations about allowing new
modifiers on constructor promoted properties parameters.
Right now there is no `final` modifier for parameters, but this means we
might not be able to allow them in the future.
In other languages (Java), it makes the parameter variable a constant (not
reassignable).

So this decision might have implications so that when we decide to make
variables or parameters not reassignable, we will not be able to use
"final" as a keyword.
Not saying there is a problem, just that we need to be aware of it, as
probably `final` is not a very good choice, and `const` or `readonly` are
probably better options.

Alex

---

## #123501 — Andreas Heigl — 2024-06-04T05:01:07+00:00

URL: https://externals.io/message/123456#123501 · In reply to: #123456

Hey Larry, hey Ilija


Am 29.05.24 um 21:15 schrieb Larry Garfield:
> As promised, Ilija and I offer this revised version of asymmetric visibility.
> 
> https://wiki.php.net/rfc/asymmetric-visibility-v2
> 
> It's still essentially the same as last year's version, but with a few adjustments and changes:
> 
> * readonly properties are now supported in a logical fashion.
> * We've brought back the abbreviated form, as public-read, something else set is the most common use case.
> * The section on magic methods has been greatly simplified.  The implementation itself hasn't changed, but the explanation is a lot less confusing now.
> * We've explained how aviz interacts with hooks (they don't, really) and with interface properties (in the obvious way), which didn't exist at the time of the last draft.
> * We've added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
> * We've added a section discussing why the prefix-style syntax was chosen.
> 
> *dons flame retardant suit*
> 
In general I do like the RFC. No need for a flame retardand suit IMO ;-)

There is only one thing that I stumbled upon which struck me as odd:

 > The set visibility, if specified explicitly, MUST be equal to or
 > lesser than the main (get) visibility. That is, protected public(set)
 > string $foo is not allowed.

Why?

Why can we not set a property as publicly writable but unreadable?

If that is a technical necessity, then so be it. But if that is a 
logical limitation, then I'm asking myself: why do we want to explicitly 
limit possible usecases? Not that I have one right now in my mind but I 
do know that we used similar settings in Filesystems for letterbox 
systems where users could copy files into other users letterbox (write 
only) without being able to later see the files.

Similar possibilities are available here here a property could be 
publicly writeable but only privately readable as read-access is only 
granted via a method.

I'm not saying it's the usualy use-case, but why explicitly disallowing it?

Cheers

Andreas

-- 
                                                               ,,,
                                                              (o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl                                                       |
| mailto:andreas@heigl.org                  N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org                                           |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas                               |
+---------------------------------------------------------------------+
| GPG-Key: https://hei.gl/keyandreasheiglorg                          |
+---------------------------------------------------------------------+

---

## #123504 — Larry Garfield — 2024-06-04T13:30:35+00:00

URL: https://externals.io/message/123456#123504 · In reply to: #123501

> There is only one thing that I stumbled upon which struck me as odd:
>
> > […]
>
> Why?
>
> Why can we not set a property as publicly writable but unreadable?
>
> If that is a technical necessity, then so be it. But if that is a 
> logical limitation, then I'm asking myself: why do we want to explicitly 
> limit possible usecases? Not that I have one right now in my mind but I 
> do know that we used similar settings in Filesystems for letterbox 
> systems where users could copy files into other users letterbox (write 
> only) without being able to later see the files.
>
> Similar possibilities are available here here a property could be 
> publicly writeable but only privately readable as read-access is only 
> granted via a method.
>
> I'm not saying it's the usualy use-case, but why explicitly disallowing it?

It's not a technical requirement.  Mainly, we just cannot think of a reason why you'd ever do that, so making some seemingly nonsensical cases illegal seems like a good guardrail.

If enough people felt strongly that it should be allowed, I don't think there's any technical reason it couldn't be allowed, other than it would allow some rather silly combinations.  (Ilija can tell me if I'm wrong.)  However, also note that it is, of course, much easier to allow more combinations in the future than to remove them, should we find they cause trouble.

--Larry Garfield

---

## #123509 — tim@bastelstu.be — 2024-06-04T17:54:16+00:00

URL: https://externals.io/message/123456#123509 · In reply to: #123504

Hi


> If enough people felt strongly that it should be allowed, I don't think there's any technical reason it couldn't be allowed, other than it would allow some rather silly combinations.  (Ilija can tell me if I'm wrong.)  However, also note that it is, of course, much easier to allow more combinations in the future than to remove them, should we find they cause trouble.

One thing that would get pretty wonky would be private-read properties: 
Private property names are currently internally "mangled" to include the 
class name. This allows to define the same private property in multiple 
classes of an inheritance chain, without those classes needing to know 
about the private properties of each other and making the addition and 
removal of a private property not a BC break. For all intents and 
purposes those private properties to not exist, unless you are the class 
itself.

I have no idea what the semantics of a public-write, private-read 
property should be - and this problem is pretty similar to the 
sibling-discussion about making private-set properties implicitly final, 
because otherwise the semantics get wonky.

I believe that the case of making a property public-write, private-read 
is best left to a virtual set-only hook.

Best regards
Tim Düsterhus

---

## #123516 — Larry Garfield — 2024-06-05T14:28:00+00:00

URL: https://externals.io/message/123456#123516 · In reply to: #123480

> 
> > […]
>
> 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 => 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)` => 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 => 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)` => 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

---

## #123521 — Claude Pache — 2024-06-05T16:56:57+00:00

URL: https://externals.io/message/123456#123521 · In reply to: #123516

> Le 5 juin 2024 à 16:28, Larry Garfield <larry@garfieldtech.com> a écrit :
> 
> 
> > […]
> 
> 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 => 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)` => 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 => 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)` => 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: “Proposal 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.”

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. From 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. From the point of view of the parent class, nothing will change, except false expectations—and 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.

—Claude

---

## #123527 — Arvids Godjuks — 2024-06-05T19:55:21+00:00

URL: https://externals.io/message/123456#123527 · In reply to: #123521

> *snip*
> 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: “Proposal 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.”
>
> 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. From 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. From the point of view of the parent class, nothing
> will change, except false expectations—and 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.
>
> —Claude
>
>
>
Hello everyone,
I've been seeing readonly bashed/blamed/being roadblock, etc, etc as in the
implementation ended up being sloppy and blocking other things or making
things hard...
While I know BC is king and stuff, why not just say "yes, this was designed
badly and we will redo it" and just do it? While there's not yet an
absolute boatload of that code out there when it would be absolutely
massive BC break? Don't repeat the mistakes of the old days :D

Cause the impression I'm getting any significant RFC now has to work around
the readonly's sloppy implementation and there's a bigger and bigger
section on that with each next RFC when there's more and more advanced
features for the OOP part of things.

-- 

Arvīds Godjuks
+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius

---

Next page: https://externals.io/message/123456.md?page=2
