Re: [RFC] [DISCUSSION] Immutable/final/readonly properties
| From: | Larry Garfield | Date: | Mon, 24 Feb 2020 20:35:15 +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-108743@lists.php.net to get a copy of this message | ||
On Mon, Feb 24, 2020, at 7:55 AM, Rowan Tommins wrote:
> On Fri, 21 Feb 2020 at 23:18, Larry Garfield <larry@garfieldtech.com> wrote:
>
> > 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.
> >
>
>
> As with typed properties, I wonder if there's a way we can introduce a new
> initialisation sequence for objects, so that there's a specific point where
> the object is considered "fully constructed" after new or clone.
>
> A couple of brainstormed ideas, with plenty of downsides I'm sure:
>
> An explicit finalise() function or keyword
>
> public function withFoo($foo) {
> $inst = clone $this;
> // all readonly properties are initially "unlocked"
> $inst->foo = $foo;
> // now lock them, perhaps also checking that no typed properties are
> left uninitialised
> finalise($inst); // or finalise $inst;
> return $inst;
> }
>
> A special code block:
>
> public function withFoo($foo) {
> $inst = clone $this {
> // all properties are "unlocked" within this special block
> $inst->foo = $foo;
> };
> // from here onwards, readonly properties can't be written to
> return $inst;
> }
>
> Perhaps could also be used with constructors:
>
> public function createFromOtherThing(OtherThing $other) {
> $inst = new static('some parameter') {
> // readonly properties can be written in the constructor, or
> within this block
> $inst->foo = $other->getFoo();
> };
> // object is "finalised" when the block ends
> return $inst;
> }
If the way to resolve this question is a special "unlocked" mode, I would definitely favor
the explicit code block. That way it's self-closing and you can't forget to do so.
(Murphy's Law: If you rely on developers remembering to do X to keep code safe, they will
promptly forget to do X.)
Also, FTR, any approach that forces developers to write a 9 parameter constructor over and over is
one I can never get behind, doubly so for something that is currently only a single line. This
isn't a case of "pain tells you what not to do"; a value object having a lot of
internal properties is a completely valid use case, and "but composition" is not an
answer.
--Larry Garfield