Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95740 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 27822 invoked from network); 7 Sep 2016 12:01:57 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 7 Sep 2016 12:01:57 -0000 Authentication-Results: pb1.pair.com smtp.mail=michal@brzuchalski.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=michal@brzuchalski.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain brzuchalski.com designates 188.165.245.118 as permitted sender) X-PHP-List-Original-Sender: michal@brzuchalski.com X-Host-Fingerprint: 188.165.245.118 ns220893.ip-188-165-245.eu Received: from [188.165.245.118] ([188.165.245.118:55678] helo=poczta.brzuchalski.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id CA/9F-18051-2B100D75 for ; Wed, 07 Sep 2016 08:01:56 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by poczta.brzuchalski.com (Postfix) with ESMTP id BF81F2984249 for ; Wed, 7 Sep 2016 14:01:50 +0200 (CEST) Received: from poczta.brzuchalski.com ([127.0.0.1]) by localhost (poczta.brzuchalski.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zKTgGIjExHhX for ; Wed, 7 Sep 2016 14:01:47 +0200 (CEST) Received: from mail-qk0-f170.google.com (unknown [209.85.220.170]) by poczta.brzuchalski.com (Postfix) with ESMTPSA id C7EBB298424A for ; Wed, 7 Sep 2016 14:01:42 +0200 (CEST) Received: by mail-qk0-f170.google.com with SMTP id w204so8992630qka.0 for ; Wed, 07 Sep 2016 05:01:42 -0700 (PDT) X-Gm-Message-State: AE9vXwPbXoZCqPkACUTEIGJ15B1t2T7oW98vrfrvp1L/jIBu3bRITWZTQimccmNHHz3MtxdUtV6OHhPyBsedLA== X-Received: by 10.55.88.199 with SMTP id m190mr50912183qkb.78.1473249701903; Wed, 07 Sep 2016 05:01:41 -0700 (PDT) MIME-Version: 1.0 Received: by 10.200.56.100 with HTTP; Wed, 7 Sep 2016 05:01:40 -0700 (PDT) In-Reply-To: References: <0e71d28e-1d64-5372-b58d-e54c7afae3b8@fleshgrinder.com> <642a6e78-90ea-cbf0-ec1c-376c24e568c5@fleshgrinder.com> <0800a5ca-3d14-c541-1a1a-2574ec802b8c@fleshgrinder.com> <83fa661e-2d3d-6548-a506-fb969be31c0e@garfieldtech.com> <69D0D60B-3D42-4228-8156-F9E6AFEDF72B@koalephant.com> <388c9bf3-d7bc-7e73-bf44-b4c446c2398b@rochette.cc> Date: Wed, 7 Sep 2016 14:01:40 +0200 X-Gmail-Original-Message-ID: Message-ID: To: =?UTF-8?Q?Silvio_Mariji=C4=87?= Cc: Stephen Reay , Mathieu Rochette , PHP Internals List Content-Type: multipart/alternative; boundary=001a114e507e25c6f0053be9adc6 Subject: Re: [PHP-DEV] RFC - Immutable classes From: michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=) --001a114e507e25c6f0053be9adc6 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi, Frozing an immutable object after it's creation is most important feature of Immutable Classes. Without it you cannot have guarantee it's internal state is consistent as it was during creation. Having methods access to write is quite dangerous - think about CLosure binded to immutable object - there is a way to change internal state of such object, and you've broken immutability in it. Changing object state is reasonable only on clones, because you newer know what references to original object. There can be cloning objects as they are now - I don't see it usefull, but if somebody really want's it, why not. IMHO providing method modificator for changing object in context of newly created clone is the best solution. The keyword for it isn't chosen yet, we may discuss it or even vote for it. For now we can call it anyway while keeping in mind it may change during voting. 2016-09-07 12:43 GMT+02:00 Silvio Mariji=C4=87 : > @Stephan > > I am against that any kind of method can make modification on original > object. > > Cloning can be allowed but for this use case where you would pass > properties that are changing, we would have to modify syntax of clone > On Sep 7, 2016 12:37 PM, "Stephen Reay" wrote: > > > Hey Matheiu, Silvio, > > > > That is my main concern with the inability to clone from outside the > > class. I don=E2=80=99t think immutable should cause an error in this si= tuation - > if > > you really don=E2=80=99t want to allow users to create objects they can= =E2=80=99t modify > > (which I understand) could clone on an immutable object from outside th= e > > class, simply return the object itself ? > > > > Re: immutable only externally - yes, as I mentioned I can understand th= at > > would be a deal-break for some. In that situation, I=E2=80=99d be happy= with some > > way to indicate that a method can make changes. Would this then mean th= at > > such a method could modify the object itself, rather than the clone? > > > > Cheers > > > > Stephen > > > > > On 7 Sep 2016, at 17:09, Mathieu Rochette wrote= : > > > > > > > > > > > > On 07/09/2016 11:28, Silvio Mariji=C4=87 wrote: > > >> Hi Stephen, > > >> > > >> Cloning is disabled at the moment in implementation because you woul= d > > end > > >> up with a object that you can not change, so you have no use of that= . > > I'll > > >> change that as soon as we find some good solution for handling that. > > Your > > >> example is not really clear to me. At what point we should unlock/lo= ck > > >> object based on your example? > > > what would happen if you tried to clone an immutable object, throw an > > error ? > > > it means that you might have to use reflection to check if the object > is > > immutable before cloning it. otherwise making a class immutable would b= e > a > > BC > > >> > > >> DateTimeImmutable does not prevent cloning because immutability is > > achieved > > >> by encapsulation, and we want to get rid of the need of encapsulatio= n > in > > >> our implementation of immutable objects. > > >> > > >> Best, > > >> Silvio. > > >> > > >> 2016-09-07 11:05 GMT+02:00 Stephen Reay : > > >> > > >>> (Sorry for any dupes, sent from wrong address originally) > > >>> > > >>> From a developer point of view, I would suggest that a feature shou= ld > > aim > > >>> to be as clear to understand with as little =E2=80=9Cmagic" as poss= ible. > > >>> > > >>> > > >>> If the goal of an immutable class is to allow public properties to = be > > made > > >>> read-only, my expectation would be that: > > >>> > > >>> - write access to any public property from outside class context, i= s > an > > >>> error. > > >>> > > >>> This seems to be pretty much accepted by everyone > > >>> > > >>> > > >>> - clone still works as expected > > >>> > > >>> There has been some suggestion that clone $immutableObj should not = be > > >>> allowed. Unless there is some specific language/engine gain by that= , > > what > > >>> is the point of having this behaviour? > > >>> Existing built-in immutable classes (like DateTimeImmutable) do not > > >>> prevent cloning, so why should this? > > >>> > > >>> - regular cloning from within class method(s) is the suggested way = to > > >>> provide =E2=80=9Ccreate a copy of the object with a new value=E2=80= =9D functionality. > > >>> > > >>> This example was given before, effectively: > > >>> > > >>> public function withValue($val) { > > >>> $clone =3D clone $this; > > >>> $clone->val =3D $val; > > >>> > > >>> return $clone; > > >>> } > > >>> > > >>> > > >>> > > >>> > > >>> > > >>>> On 7 Sep 2016, at 13:57, Micha=C5=82 Brzuchalski > > > >>> wrote: > > >>>> 06.09.2016 9:13 PM "Fleshgrinder" > napisa=C5=82(a): > > >>>>> I understand the concerns of all of you very well and it's nice t= o > > see a > > >>>>> discussion around this topic. Fun fact, we are not the only ones > with > > >>>>> these issues: https://github.com/dotnet/roslyn/issues/159 > > >>>>> > > >>>>> On 9/6/2016 6:01 PM, Larry Garfield wrote: > > >>>>>> How big of a need is it to allow returning $this instead of > $clone, > > >>>>>> and/or can that be internalized somehow as well? With > > copy-on-write, > > >>>>>> is that really an issue beyond a micro-optimization? > > >>>>> I asked the same question before because I am also unable to answ= er > > this > > >>>>> question regarding the engine. > > >>>>> > > >>>>> However, for me it is more than micro-optimization, it is about > > >>> identity. > > >>>>> final class Immutable { > > >>>>> // ... the usual ... > > >>>>> public function withValue($value) { > > >>>>> $clone =3D clone $this; > > >>>>> $clone->value =3D $value; > > >>>>> return $clone; > > >>>>> } > > >>>>> } > > >>>>> > > >>>>> $red =3D new Immutable('red'); > > >>>>> $still_red =3D $red->withValue('red'); > > >>>>> > > >>>>> var_dump($red =3D=3D=3D $still_red); // bool(false) > > >>>>> > > >>>>> This is a problem in terms of value objects and PHP still does no= t > > allow > > >>>>> us operator overloading. A circumstance that I definitely want to > > >>>>> address in the near future. > > >>>>> > > >>>>> But the keyword copy-on-write leads me to yet another proposal, > > actually > > >>>>> your input led me to two new proposals. > > >>>>> > > >>>>> # Copy-on-Write (CoW) > > >>>>> Why are we even bothering on finding ways on making it hard for > > >>>>> developers while the solution to our problem is directly in front > of > > us: > > >>>>> PHP Strings! > > >>>>> > > >>>> AFAIK CoW in case of objects would be impossible to implement. > > >>>> > > >>>>> Every place in a PHP program refers to the same string if that > > string is > > >>>>> the same string. In the second someone mutates that string in any > way > > >>>>> she gets her own mutated reference to that string. > > >>>>> > > >>>>> That's exactly how we could deal with immutable objects. Develope= rs > > do > > >>>>> not need to take care of anything, they just write totally normal > > >>>>> objects and the engine takes care of everything. > > >>>>> > > >>>>> This approach also has the advantage that the return value of any > > method > > >>>>> is (as always) up to the developers. > > >>>>> > > >>>>> (Cloning is disabled and results in an error as is because it mak= es > > no > > >>>>> sense at all.) > > >>>>> > > >>>>> # Identity > > >>>>> This directly leads to the second part of my thoughts and I alrea= dy > > >>>>> touched that topic: identity. If we have two strings their binary > > >>>>> representation is always the same: > > >>>>> > > >>>>> var_dump('string' =3D=3D=3D 'string'); // bool(true) > > >>>>> > > >>>>> This is the exact behavior one wants for value objects too. Hence= , > > >>>>> immutable objects should have this behavior since they identify > > >>>>> themselves by their values and not through instances. If I create > two > > >>>>> instances of Money with the amount 10 and the Currency EUR then > they > > are > > >>>>> always the same, no matter what. This would also mean that no > > developer > > >>>>> ever needs to check if the new value is the same as the existing > one, > > >>>>> nor does anyone ever has to implement the flyweight pattern for > > >>>>> immutable objects. > > >>>>> > > >>>>> A last very important attribute is that it does not matter in whi= ch > > >>>>> thread an immutable value object is created because it always has > the > > >>>>> same identity regardless of it. > > >>>>> > > >>>>> This could easily be achieved by overwriting the object hashes > > >>>>> (spl_object_hash) with something that hashes based on the values, > and > > >>>>> predictably across threads (UUIDs?). > > >>>>> > > >>>>> # Full Example > > >>>>> > >>>>> > > >>>>> final immutable class ValueObject { > > >>>>> > > >>>>> public $value; > > >>>>> > > >>>>> public function __construct($value) { > > >>>>> $this->value =3D $value; > > >>>>> } > > >>>>> > > >>>>> public function withValue($value) { > > >>>>> $this->value =3D $value; > > >>>>> } > > >>>>> > > >>>>> } > > >>>>> > > >>>>> class A { > > >>>>> > > >>>>> public $vo; > > >>>>> > > >>>>> public function __construct(ValueObject $vo) { > > >>>>> $this->vo =3D $vo; > > >>>>> } > > >>>>> > > >>>>> } > > >>>>> > > >>>>> class B { > > >>>>> > > >>>>> public $vo; > > >>>>> > > >>>>> public function __construct(ValueObject $vo) { > > >>>>> $this->vo =3D $vo; > > >>>>> } > > >>>>> > > >>>>> } > > >>>>> > > >>>>> $vo =3D new ValueObject(1); > > >>>>> > > >>>>> $a =3D new A($vo); > > >>>>> $b =3D new B($vo); > > >>>>> > > >>>>> var_dump($a->vo =3D=3D=3D $b->vo); // bool(true) > > >>>>> > > >>>>> $a->vo->withValue(2); > > >>>>> > > >>>>> var_dump($a->vo =3D=3D=3D $b->vo); // bool(false) > > >>>>> > > >>>>> $a->vo->withValue(1); > > >>>>> > > >>>>> var_dump($a->vo =3D=3D=3D $b->vo); // bool(true) > > >>>>> > > >>>>> // :) > > >>>>> > > >>>>> ?> > > >>>>> > > >>>>> -- > > >>>>> Richard "Fleshgrinder" Fussenegger > > >>>>> > > >>> > > >>> -- > > >>> PHP Internals - PHP Runtime Development Mailing List > > >>> To unsubscribe, visit: http://www.php.net/unsub.php > > >>> > > >>> > > >> > > > > > > > > --=20 regards / pozdrawiam, -- Micha=C5=82 Brzuchalski brzuchalski.com --001a114e507e25c6f0053be9adc6--