Re: RFC - Immutable classes

From: Date: Fri, 02 Sep 2016 14:27:47 +0000
Subject: Re: RFC - Immutable classes
References: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  Groups: php.internals 
Request: Send a blank email to internals+get-95566@lists.php.net to get a copy of this message
On 09/02/2016 09:06 AM, Silvio Marijić wrote:
Well at the moment expection is thrown in case when you try to clone immutable object. But you do seem to have valid point there regarding __clone method. I'm definitely going to give it a thought. Best, Silvio. 2016-09-02 15:52 GMT+02:00 André Rømcke <andre.romcke@ez.no>:
On Sep 2, 2016, at 09:10 , Silvio Marijić <marijic.silvio@gmail.com>
wrote:
Hi Fleshgrinder, Since Michal answered most of the questions, I'll just add some notes. Initially I added restrictions to abstract classes, but I did think about that over the last couple of days and couldn't find any concrete reason
for
that restriction, so I think I'm going to remove that. As far as cloning, it is disabled for immutable objects, because you'll end up with the copy of object that you can not modify. I did mention in Cons sections that cloning is disabled, maybe it should be made more clear.
_If_ there are use-cases for it, wouldn’t it also be safe that the clone is allowed to be modified during __clone() and afterwards sealed? Like in __construct(). And if you don’t want to allow cloning, throw in __clone. Best, André
I'd have to agree here. I love the idea of "lockable" immutable objects. However, the __clone() method has to be a modifiable area just like __construct() or else it's effectively useless for anything more than a trivial object. This was one of the main concerns with immutability in the PSR-7 discussions. Consider this sample class, with 8 properties (entirely reasonable for a complex value object): immutable class Record { public $a; public $b; public $c; public $d; public $e; public $f; public $g; public $h; public function __construct($a, $b, $c, $d, $e, $f, $g, $h) {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
    $this->d = $d;
    $this->e = $e;
    $this->f = $f;
    $this->g = $g;
    $this->h = $h;
} } Now I want a new value object that is the same, except that $d is incremented by 2. That is, I'm building up the value object over time rather than knowing everything at construct time. (This is exactly the use case of PSR-7.) I have to do this: $r1 = new Record(1, 2, 3, 4, 5, 6, 7, 8); $r2 - new Record($r1->a, $r1->b, $r1->c, $r1->d + 2, $1->e, $r1->f, $r1->g, $r1->h); That's crazy clunky, and makes immutable objects not very useful. Imagine a money object where you had to dissect it to its primitives, tweak one, and then repackage it just to add a dollar figure to it. That's not worth the benefit of being immutable. The way PSR-7 addressed that (using fake-immutability, basically), was this: class Response { // ... protected $statusCode; public function withStatusCode($code) {
    $new = clone($this);
    $new->statusCode = $code;
    return $new;
} } That is, outside of the object there's no way to modify it in place, but it becomes super easy to get a slightly-modified version of the object: $r2 = $r1->withStatusCode(418); And because of PHP's copy-on-write support, it's actually surprisingly cheap. For language-level immutable objects, we would need some equivalent of that behavior. I'm not sure exactly what form it should take (explicit lock/unlock commands is all I can think of off hand, which I dislike), but that's the use case that would need to be addressed. --Larry Garfield

« previous php.internals (#95566) next »