Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95751 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 55947 invoked from network); 7 Sep 2016 13:24:25 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 7 Sep 2016 13:24:25 -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:32887] helo=texthtml.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id E1/81-43559-60510D75 for ; Wed, 07 Sep 2016 09:24:23 -0400 Received: by texthtml.net (Postfix, from userid 65534) id 03B7C230; Wed, 7 Sep 2016 13:24:17 +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,HTML_MESSAGE 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 E260C23C; Wed, 7 Sep 2016 13:24:01 +0000 (UTC) To: =?UTF-8?Q?Micha=c5=82_Brzuchalski?= References: <4f54308a-4a69-2e6b-2ed0-51d4336d1cd4@fleshgrinder.com> <5969d1af-48e5-1376-07fe-9568de538145@texthtml.net> <0e71d28e-1d64-5372-b58d-e54c7afae3b8@fleshgrinder.com> Cc: PHP Internals List Message-ID: Date: Wed, 7 Sep 2016 15:24:01 +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: multipart/alternative; boundary="------------F37BAF919753D1BE1D458386" Content-Language: en-US Subject: Re: [PHP-DEV] RFC - Immutable classes From: mathieu@rochette.cc (Mathieu Rochette) --------------F37BAF919753D1BE1D458386 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit On 07/09/2016 15:09, Michał Brzuchalski wrote: > @Mathieu How about that: > https://gist.github.com/brzuchal/e7b721e22a19cca42ec0d1f597a23baf > We've discussed this could be best option, when invoking `transformer` > method (or whatever we call it) > there is `$this = clone $this` invoked under the hood. I don't know, it seems a bit strange to override $this . I can't have the original object and the new one at the same time. Or do you mean I also have the option to do "$clone = clone $this" ? > > 2016-09-07 14:53 GMT+02:00 Mathieu Rochette >: > > a few remarks on mutator methods: > > * It could be a nice way to solve the "create another one almost > the same" use case. > * I don't mind if $clone is an explicit parameter or magically > available > * what happens if I call other function/methods with this $clone > before the end of the function ? > * and the only downside: I have to make a method just for > cloning. that means I have to call a mutator multiple times if > I want to make a bunch of clone, eg: > > because of the last point, I think I'd like the seal the clone at > then end of the block/method better, here are 2 examples to > illustrate what I mean > > class immutable foo { > private $prop = 0; > > public function __construct($v) {$this->prop = $v;} > > public function bar(obj $o) { > $e->makeSomethingWith($this->cloneAndEdit(42)); > } > > public function many($n) { > $a = []; > for ($i = 0; $i < $n; $i++) { > $a[] = $this->cloneAndEdit($i)); > } > return $a; > > } > > public function mut cloneAndEdit($n) { > $clone->prop = $n; > } > } > > // vs > > class immutable foo { > private $prop = 0; > > public function __construct($v) {$this->prop = $v;} > > public function bar(obj $o) { > $c = $clone $this; > $c->prop = 42; > $e->makeSomethingWith($c); > } > > public function many($n) { > $a = []; > for ($i = 0; $i < $n; $i++) { > $a[] = $c = $clone $this; > $c->prop = 42; > } > return $a; > } > } > > I understand that it's still not clear exactly when the object > should be sealed but if it can works this one would have my preference > > On 04/09/2016 14:10, Michał Brzuchalski wrote: >> 2016-09-04 10:55 GMT+02:00 Fleshgrinder : >> >>> Hi Chris! >>> >>> On 9/3/2016 5:00 PM, Chris Riley wrote: >>>> - 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) >>>> >>> The constraint b) would make the object mutable and defeat the purpose >>> of the immutable modifier since any property could change at any time if >>> it was NULL at the beginning. Requiring syncing in concurrent environments. >>> >>> On 9/3/2016 5:00 PM, Chris Riley wrote: >>>> - Arrays assigned to immutable properties would not be possible to change >>>> >>> Array support would definitely be very nice. I mean, we have constant >>> arrays already, hence, it is possible. >>> >>> On 9/3/2016 5:00 PM, Chris Riley wrote: >>>> - Objects assigned to immutable properties would be possible to change, >>> so >>>> long as the same object remained assigned to the property. >>>> >>> This would once more lead to mutability and the constraint of >>> immutability would be violated. >>> >>> On 9/3/2016 5:00 PM, Chris Riley wrote: >>>> 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. >>>> >>> The adoption of the feature will be halted until 7.2 is widely available >>> in bigger projects. That is most certainly right. However, we should aim >>> for the best, most useful, and future proof solution and not towards the >>> one that's adopted very fast but lacks some important constraints. >>> Having truly immutable objects is required in concurrent scenarios and >>> such scenarios are in the future for PHP and not in the past. >>> >>> Regarding setter injection: I do not see the need for it at all in the >>> context of immutable objects. In the end we are talking about value >>> objects here and they should not have any optional dependencies. Maybe >>> you could come up with a use case to illustrate the need? >>> >>> On 9/3/2016 5:00 PM, Chris Riley wrote: >>>> 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 = $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 >>>> >>> How does one know which property is to be mutated in the __with method? >>> You should also not underestimate the performance hit and the branching >>> since you only want to change the properties that changed based on the >>> data from the passed array. >>> >>> I have a third proposal after giving this some more thought. Inspired by >>> Rust's approach to mark mutation explicitly. >>> >>> final immutable class ValueObject { >>> >>> public $value; >>> >>> public mutator [function] withValue($clone, $value): static { >>> $clone->value = $value; >>> } >>> >>> } >>> >>> >> Providing `mutator` | `mut` keyword as method modifier sounds liek a good >> idea, >> althought passing `$clone` parameter as first additional param could break >> method declaration and would be misleading. >> >> Assuming mutator method is designed to return mutated clone of immutable >> object >> having `$clone` variable could be handled internally without breaking >> method declaration. >> >> Such variable could be unlocked while in mutator method and locked on >> return. >> I was thinking about additional check if such mutator returns `$clone` but >> not `$this` >> but I don't see the need of it - assuming there is no what to change in some >> circumstances ther would be also possible to return `$this`. >> >> The return type declaration `self` could increase readability, but should >> not be required, >> as some developers doesn't already use return types. >> >> >>> A mutator function always receives the mutable clone as first argument >>> and always returns that one. Users can have a return but they must >>> return the clone (hence the static return type declaration). >>> >>> $vo1 = new ValueObject(1); >>> $vo2 = $vo1->withValue(2); >>> >>> Calls are of course without the clone as it is handled by the engine. >>> There is no special branching necessary and no performance hit at all >>> while the logic is kept in the place where it is required. >>> >>> -- >>> Richard "Fleshgrinder" Fussenegger >>> >>> > > > > > -- > regards / pozdrawiam, > -- > Michał Brzuchalski > brzuchalski.com --------------F37BAF919753D1BE1D458386--