Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95600 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 46845 invoked from network); 3 Sep 2016 15:00:46 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 3 Sep 2016 15:00:46 -0000 Authentication-Results: pb1.pair.com header.from=t.carnage@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=t.carnage@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 74.125.82.46 as permitted sender) X-PHP-List-Original-Sender: t.carnage@gmail.com X-Host-Fingerprint: 74.125.82.46 mail-wm0-f46.google.com Received: from [74.125.82.46] ([74.125.82.46:36450] helo=mail-wm0-f46.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id FF/A1-32927-A95EAC75 for ; Sat, 03 Sep 2016 11:00:45 -0400 Received: by mail-wm0-f46.google.com with SMTP id c133so66568423wmd.1 for ; Sat, 03 Sep 2016 08:00:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=axqGX2GDtWeUnEs15uLg2tq+u/XLEBdrg+P2OxEC0rQ=; b=bRpzS8rMgQ1WsValOYgcKUkqj/Px0JI9GozJYEtBwTZs+hCaXkxYKraud3KPoFASeM 4oTvgSND0b6dRvwoMN/gA3nHR02ODWH4O8UXE/9eqR0OzJHJzEMdHVCxZrRaobJnLHMe Tzzdmn6be8SXSOXCa9qfLd3sBfx//VKZ/uUAAN23SBTmsaSCEs+UaytM5cpiXmAUjlxz EIFYUwae/RwKU8CdwhR/9SSe65AGLh1EoPOFh5kju08xysEzTm2tQ5PExFR+BdwhY7k8 yohHEnkYWK6+ovQ0wbbZHe7wbr7KPx2uJO2E5KNDD7rdzJBmZbfLC4DIsTG6wlYts1fG IUiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=axqGX2GDtWeUnEs15uLg2tq+u/XLEBdrg+P2OxEC0rQ=; b=SbSmOhD9RZRNh7Rbd4xVFdQVe4wPZlq0vPpq7z64jnTEUmIsvz6TDGBWzFE0qITndy QJSP/AAXPTPJH43CyMO32UugSRWMHSOymiM3hiFnUsGKTg7waiCopwN6EyU9qKUtTaao ksWIE+3KkJ9RRQ8ZJy5v1XgiNVotI93Tk3zHdti5LKtRRehr3ux3VB2Yu0tQ8yfSN6NH L4+VcgJ3QshuTywa1wRV3CXWHIvVKEN2wLKZSA9YxWPqUIeSX6CfEMUoRhANLJnDm6ao SSq0TBONv6nmhwgg9k2y+LYL5uJvhbpF5yMfr5yX7k9beUhuPeC5q9xq89ifBrk8MQfG Qt1w== X-Gm-Message-State: AE9vXwPOC+MGk/e7fBKGkGVk+YEpGYo7hlAMxzXOk3adBHSPyYoAj08qFaEWzp231YtVRQ4RAoe6HSSNO4Zzzg== X-Received: by 10.28.111.4 with SMTP id k4mr8368638wmc.94.1472914837572; Sat, 03 Sep 2016 08:00:37 -0700 (PDT) MIME-Version: 1.0 Received: by 10.194.113.10 with HTTP; Sat, 3 Sep 2016 08:00:36 -0700 (PDT) In-Reply-To: References: <167d6432-e4d6-d87d-5d31-d3d82a8de4ce@fleshgrinder.com> <99F80C06-654D-4109-BE07-2FA5B1073E5D@ez.no> <4f54308a-4a69-2e6b-2ed0-51d4336d1cd4@fleshgrinder.com> <5969d1af-48e5-1376-07fe-9568de538145@texthtml.net> Date: Sat, 3 Sep 2016 16:00:36 +0100 Message-ID: To: =?UTF-8?Q?Silvio_Mariji=C4=87?= Cc: =?UTF-8?Q?Micha=C5=82_Brzuchalski?= , Larry Garfield , PHP Internals List Content-Type: multipart/alternative; boundary=001a1146a0dcad922f053b9bb5a3 Subject: Re: [PHP-DEV] RFC - Immutable classes From: t.carnage@gmail.com (Chris Riley) --001a1146a0dcad922f053b9bb5a3 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable On 2 September 2016 at 15:59, Silvio Mariji=C4=87 wrote: > Michal I'm talking about __clone() callback after clone operation. But I > agree with you about syntax part. > > 2016-09-02 16:46 GMT+02:00 Micha=C5=82 Brzuchalski < > michal.brzuchalski@gmail.com> > : > > > 02.09.2016 16:29 "Larry Garfield" napisa=C5=82= (a): > > > > > > On 09/02/2016 09:06 AM, Silvio Mariji=C4=87 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 regardin= g > > >> __clone method. I'm definitely going to give it a thought. > > >> > > >> Best, > > >> Silvio. > > >> > > >> 2016-09-02 15:52 GMT+02:00 Andr=C3=A9 R=C3=B8mcke : > > >> > > >>> > > >>>> On Sep 2, 2016, at 09:10 , Silvio Mariji=C4=87 > > > >>> > > >>> 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 thin= k > > 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 t= he > > 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=E2=80=99t it also be safe t= hat the > > clone > > >>> is allowed to be modified during __clone() and afterwards sealed? > Like > > in > > >>> __construct(). > > >>> And if you don=E2=80=99t want to allow cloning, throw in __clone. > > >>> > > >>> Best, > > >>> Andr=C3=A9 > > > > > > > > > I'd have to agree here. I love the idea of "lockable" immutable > > objects. However, the __clone() method has to be a modifiable area jus= t > > 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 =3D $a; > > > $this->b =3D $b; > > > $this->c =3D $c; > > > $this->d =3D $d; > > > $this->e =3D $e; > > > $this->f =3D $f; > > > $this->g =3D $g; > > > $this->h =3D $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 =3D 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), wa= s > > this: > > > > > > class Response { > > > // ... > > > > > > protected $statusCode; > > > > > > public function withStatusCode($code) { > > > $new =3D clone($this); > > > $new->statusCode =3D $code; > > > return $new; > > > } > > > } > > > > > > > I see only way in somehow invoking closere with cloning. That'll need > > additional syntax. Clone is left side operator not a function - it's no= t > > being called with parenthesis. If this was an object method accessible > from > > public it coud gace such closure injected... > > > > > 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 =3D $r1->withStatusCode(418); > > > > > > And because of PHP's copy-on-write support, it's actually surprisingl= y > > cheap. > > > > > > For language-level immutable objects, we would need some equivalent o= f > > 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 > > > > > > > > > -- > > > PHP Internals - PHP Runtime Development Mailing List > > > To unsubscribe, visit: http://www.php.net/unsub.php > > > > > > > > > -- > Silvio Mariji=C4=87 > Software Engineer > 2e Systems > The issue with handling updates on clone to immutable objects was the reason I left the proposal to fade away last time. Your RFC is fairly close to what I originally had in mind with some small tweaks. To sum up what my proposal was: - Properties can be declared immutable. Immutable properties may only be changed under two circumstances: a) In the objects constructor b) If they are null (This enables setter injection if required) - Classes can be declared immutable. This is a short hand for making every property immutable but also prevents any dynamic properties (eg not declared at compile time) from being set outside of the constructor. - Arrays assigned to immutable properties would not be possible to change - Objects assigned to immutable properties would be possible to change, so long as the same object remained assigned to the property. The two key differences between my ideas and your current RFC are 1) No limitation against assigning mutable objects to immutable properties 2) Allow setting of properties which are currently unset (eg null) at any point. From a developer adoption point of view, I think these two points are important to making immutable classes generally useful. Without 1, it will be a nuisance to use 3rd party libraries esp those which retain compatibility for PHP < 7.2. Without 2 you block the ability to use setter injection, which I personally would be in favour of if it meant that devs stopped using it - it wouldn't - they would simply not use immutable classes, loosing the benefits thereof. Dealing with the clone issue some of my ideas since then were: - Seal/Unseal (As per Larry's suggestion) - Parameters to __clone; in this instance the clone method would be allowed to change properties of the object as well as the constructor. This feels like it may breach the principal of least surprise as cloning an object no longer guarantees an exact copy. - A new magic method __mutate($property, $newvalue) called instead of a fatal error when a property is changed. This probably lays too many traps for developers for it to be a good idea. - Implicitly returning a new object whenever a property is changed. Similar reservations to the above. - A new magic method __with($newInstance, $args) and a keyword with that is used in place of clone eg $x =3D $y with ($arg1, $arg2); in this instance, __with receives a clone of $y (after calling __clone) and an array [$arg1, $arg2] the with magic method is allowed to mutate $newInstance and must return it. This is currently my favoured solution ~C --001a1146a0dcad922f053b9bb5a3--