Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95731 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 97689 invoked from network); 7 Sep 2016 10:12:26 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 7 Sep 2016 10:12:26 -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:41765] helo=texthtml.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 76/7C-18051-908EFC75 for ; Wed, 07 Sep 2016 06:12:25 -0400 Received: by texthtml.net (Postfix, from userid 65534) id 7C564235; Wed, 7 Sep 2016 10:12:16 +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=ham 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 D21CF233 for ; Wed, 7 Sep 2016 10:12:00 +0000 (UTC) To: internals@lists.php.net 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> Message-ID: <3bbd1cd2-23a0-25da-1fca-4df133672b98@rochette.cc> Date: Wed, 7 Sep 2016 12:12:00 +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: <69D0D60B-3D42-4228-8156-F9E6AFEDF72B@koalephant.com> 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:05, Stephen Reay wrote: > (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: I don't think this is the goal, in immutable classes, the object itself should not be able to alter itself either (whatever the visibility of the property is) > > - 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 >>> >