Re: [RFC] [DISCUSSION] Immutable/final/readonly properties
| From: | Larry Garfield | Date: | Sun, 23 Feb 2020 17:06:13 +0000 |
| Subject: | Re: [RFC] [DISCUSSION] Immutable/final/readonly properties | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-108731@lists.php.net to get a copy of this message | ||
On Sun, Feb 23, 2020, at 2:39 AM, Mike Schinkel wrote:
> > On Feb 21, 2020, at 6:17 PM, Larry Garfield <larry@garfieldtech.com> wrote:
> > I'm totally on board for better value object support, so that's a good motive
> > for me. The question I have is whether this is really a good stepping stone in that direction or if
> > it would lead down a wrong path and lock us into too much TIMTOWTDI (for the Perl fans in the room).
> > So let's think that through down that path. How would write-once properties lead into
> > properly immutable value objects? Or do they give us that themselves?
> >
> > The biggest challenge for immutable objects, IMO, is evolving them. Eg,
> > $result->withContentType(...) to use the PSR-7 example. Would we expect people to do it with a
> > method like that, or would there be some other mechanism? If the properties are public, would we
> > offer a more syntactic way to modify them directly?
> >
> > The with*() method style requires cloning the object. What happens to the locked status
> > of a set property if the object is cloned? Are they then settable again, or do they come
> > pre-locked?
> >
> > Neither of those seem good, now that I think about it. If they come pre-locked, then you
> > really can't clone, change one property, and return the new one (as is the standard practice
> > now in that case). If they don't come pre-locked, then the newly created object can have
> > everything on it changed, once, which creates a loophole. I'm not sure what the right answer
> > is here.
> >
> > My other concern is a public property (the most likely use case) would have to be set in
> > the constructor. If it's not, then callers cannot rely on it having been set yet if it's
> > set lazily. And if code inside the class tries to set it lazily, it may already have been set by
> > some external code (rightly or wrongly) and cause a failure.
>
> >
> > How do we address that? There's absolutely use cases where setting everything in the
> > constructor ahead of time is what you'd do anyway, but there are plenty where you wouldn't
> > want to, either, which creates a race condition for who sets it first, or tries to access it before
> > it gets set, etc. (This is where my repeated questions about lazy initialization come from.)
>
>
> I have struggled to follow this RFC thread fully, so if I am getting
> something out of context, please note that and I apologize in advance.
>
> However, it would see that rules for
write once properties to
> support
> lazy loading would be rather simple:
>
> 1. Write-once properties can only be updated once.
> 2. Write-once properties can only be updated within the class where
> they are declared.
This is the common use case I think many envision, but nothing in the proposal requires that. A
public write-once property (as currently written) would be world-readable, and world-writeable,
once.
Separate visibility for internal and external access is a separate matter. (Also potentially
useful, but not part of the write-once proposal at the moment.)
> 3. If you want to update a property from outside the class, create a
> set_<property>() method to allow it to happen.
> 4. If you do not want it to be set externally, do not implement a
> set_<property>() method.
> 5. If you want it to be implemented externally sometimes but not
> others, implement guard classes inside the set_<property>() method.
>
> I think that addresses all scenarios, no?
>
> -Mike
It does not.
1) Race condition if I assume that a public write-once property is a materialized value, but access
it before it gets materialized.
2) Race condition if internal non-constructor code wants to set the value, but some external routine
has set it first.
3) Cloning creates an interesting and complicated case of both of the above. Does a cloned object
start with its write-once bits reset or no? There's problems both ways.
Making a write-once property implicitly write-only-from-inside-the-class would help address point 2,
but not points 1 or 3.
Adding separate get/set visibility modifiers is another interesting idea, but is separate and should
be evaluated on its own merits. It is not at this time part of this proposal.
--Larry Garfield