Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95730 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 95962 invoked from network); 7 Sep 2016 10:10:27 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 7 Sep 2016 10:10:27 -0000 Authentication-Results: pb1.pair.com smtp.mail=mathieu@rochette.cc; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=mathieu@rochette.cc; sender-id=pass Received-SPF: pass (pb1.pair.com: domain rochette.cc designates 62.210.206.189 as permitted sender) X-PHP-List-Original-Sender: mathieu@rochette.cc X-Host-Fingerprint: 62.210.206.189 texthtml.net Received: from [62.210.206.189] ([62.210.206.189:41573] helo=texthtml.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 72/2C-18051-097EFC75 for ; Wed, 07 Sep 2016 06:10:26 -0400 Received: by texthtml.net (Postfix, from userid 65534) id B7E56233; Wed, 7 Sep 2016 10:10:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on a05d8528c5e6 X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED autolearn=unavailable autolearn_force=no version=3.4.1 Received: from [192.168.1.130] (stunnel_mail_1.mail_default [172.29.0.4]) (Authenticated sender: mathieu@texthtml.net) by texthtml.net (Postfix) with ESMTPA id D6459262; Wed, 7 Sep 2016 10:09:50 +0000 (UTC) To: =?UTF-8?Q?Silvio_Mariji=c4=87?= , Stephen Reay 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> Cc: PHP Internals List Message-ID: <388c9bf3-d7bc-7e73-bf44-b4c446c2398b@rochette.cc> Date: Wed, 7 Sep 2016 12:09:50 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:50.0) Gecko/20100101 Thunderbird/50.0a2 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-Language: en-US Subject: Re: [PHP-DEV] RFC - Immutable classes From: mathieu@rochette.cc (Mathieu Rochette) On 07/09/2016 11:28, Silvio Marijić wrote: > Hi Stephen, > > Cloning is disabled at the moment in implementation because you would 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/lock > 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 be a BC > > DateTimeImmutable does not prevent cloning because immutability is achieved > by encapsulation, and we want to get rid of the need of encapsulation 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 should aim >> to be as clear to understand with as little “magic" as possible. >> >> >> 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, is 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 “create a copy of the object with a new value” functionality. >> >> This example was given before, effectively: >> >> public function withValue($val) { >> $clone = clone $this; >> $clone->val = $val; >> >> return $clone; >> } >> >> >> >> >> >>> On 7 Sep 2016, at 13:57, Michał Brzuchalski >> wrote: >>> 06.09.2016 9:13 PM "Fleshgrinder" napisał(a): >>>> I understand the concerns of all of you very well and it's nice to 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 answer 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 = clone $this; >>>> $clone->value = $value; >>>> return $clone; >>>> } >>>> } >>>> >>>> $red = new Immutable('red'); >>>> $still_red = $red->withValue('red'); >>>> >>>> var_dump($red === $still_red); // bool(false) >>>> >>>> This is a problem in terms of value objects and PHP still does not 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. Developers 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 makes no >>>> sense at all.) >>>> >>>> # Identity >>>> This directly leads to the second part of my thoughts and I already >>>> touched that topic: identity. If we have two strings their binary >>>> representation is always the same: >>>> >>>> var_dump('string' === '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 which >>>> 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 = $value; >>>> } >>>> >>>> public function withValue($value) { >>>> $this->value = $value; >>>> } >>>> >>>> } >>>> >>>> class A { >>>> >>>> public $vo; >>>> >>>> public function __construct(ValueObject $vo) { >>>> $this->vo = $vo; >>>> } >>>> >>>> } >>>> >>>> class B { >>>> >>>> public $vo; >>>> >>>> public function __construct(ValueObject $vo) { >>>> $this->vo = $vo; >>>> } >>>> >>>> } >>>> >>>> $vo = new ValueObject(1); >>>> >>>> $a = new A($vo); >>>> $b = new B($vo); >>>> >>>> var_dump($a->vo === $b->vo); // bool(true) >>>> >>>> $a->vo->withValue(2); >>>> >>>> var_dump($a->vo === $b->vo); // bool(false) >>>> >>>> $a->vo->withValue(1); >>>> >>>> var_dump($a->vo === $b->vo); // bool(true) >>>> >>>> // :) >>>> >>>> ?> >>>> >>>> -- >>>> Richard "Fleshgrinder" Fussenegger >>>> >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php >> >> >