Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:95992 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 82303 invoked from network); 13 Sep 2016 21:38:34 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 13 Sep 2016 21:38:34 -0000 Authentication-Results: pb1.pair.com header.from=mathieu@texthtml.net; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=mathieu@texthtml.net; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain texthtml.net designates 62.210.206.189 as permitted sender) X-PHP-List-Original-Sender: mathieu@texthtml.net X-Host-Fingerprint: 62.210.206.189 texthtml.net Received: from [62.210.206.189] ([62.210.206.189:49384] helo=texthtml.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 97/74-60695-7D178D75 for ; Tue, 13 Sep 2016 17:38:32 -0400 Received: by texthtml.net (Postfix, from userid 65534) id 0D98F230; Tue, 13 Sep 2016 21:38:27 +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.0.101] (stunnel_mail_1.mail_default [172.29.0.4]) (Authenticated sender: mathieu@texthtml.net) by texthtml.net (Postfix) with ESMTPA id 6B02924D; Tue, 13 Sep 2016 21:38:11 +0000 (UTC) To: Larry Garfield , internals@lists.php.net References: <83fa661e-2d3d-6548-a506-fb969be31c0e@garfieldtech.com> <56acc1d9-f424-a460-59be-3a9a1a74b198@fleshgrinder.com> <95832b08-ee80-18c1-a3da-202eed51903e@fleshgrinder.com> <2c115733-8fe4-5230-a9ec-9d5f2cc2b810@garfieldtech.com> <06ce2daf-4112-3464-b6d6-47f1ece05303@garfieldtech.com> Message-ID: <26794926-90fc-d102-91c9-948ffe9a6665@texthtml.net> Date: Tue, 13 Sep 2016 23:38:10 +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: <06ce2daf-4112-3464-b6d6-47f1ece05303@garfieldtech.com> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Content-Language: en-US Subject: Re: [PHP-DEV] RFC - Immutable classes From: mathieu@texthtml.net (Mathieu Rochette) On 09/13/2016 02:07 AM, Larry Garfield wrote: > On 09/12/2016 06:47 PM, Stephen Reay wrote: >>> Ah, I did see that one, but there was a lot of discussion after it >>> so I thought the idea evolved. Response below based on skimming the >>> responses after that as well... >>> >>> It sounds like there's more that needs to go on, though. It sounds >>> like that thread is suggesting that $this in a method of an >>> immutable object is always cloned, which seems excessive to me. >>> >>> The point about identity is well-taken. However, it's more complex >>> on objects because equality is not as simple as it is on strings. >>> Two objects can be logically identical but physically different. For >>> instance: >>> >>> class HttpHeaders { >>> public $attributes = []; >>> } >>> >>> $a = new HttpHeaders(); >>> $a->attributes['foo'] = 'bar'; >>> $a->attributes['baz'] = 'buzz'; >>> >>> $b = new HttpHeaders(); >>> $b->attributes['baz'] = 'buzz'; >>> $b->attributes['foo'] = 'bar'; >>> >>> $a and $b are now not physically identical, since their attributes >>> array is in a different order. However, header order doesn't matter >>> in HTTP, or rather isn't supposed to. (Modulo buggy >>> implementations, of course.) So are $a and $b identical? I could >>> very easily argue both directions on that. Physical identity would >>> be easier to automate checking in the engine; logical identity would >>> require user-space code to make such decisions. >>> >>> From the discussion of "transformer" methods, I'd propose a >>> slightly different keyword. To wit: >>> >>> 1) A class marked as "immutable" may not have any of its properties >>> altered, EXCEPT in certain unlocked scopes. The constructor is an >>> unlocked scope. >>> >>> 2) A method may be marked "clone": public clone function foo() {}. A >>> clone method is identical to any other method except that A) $this >>> in its scope is not the original object, but the result of >>> clone($this); B) A clone method is an unlocked scope, so modifying >>> $this (the clone) is legal. That is more self-descriptive than >>> "transformer", and also doesn't require a new keyword. (By >>> implication, clone and static are mutually exclusive since clone >>> requires an object to clone.) >>> >>> I don't know that there are any other unlocked scopes to consider... >>> >>> #2 does leave us with the identity question that Richard raises, in >>> that returning an unmodified $this from a clone method still creates >>> a new object identity. However, I posit that is to be expected. In >>> Python, eg, while some built-in objects are immutable they are not, >>> necessarily, always the same physical object in memory. (999+1 is >>> not the same object as 1000, but 1+2 and 2 likely will be due to >>> internal engine optimizations.) You need to do a value comparison >>> of them. >>> >>> I don't see the identity case being resolved without an __identity() >>> method, or similar. Which could be useful in its own right so I'm >>> not necessarily against it, but it's an extra, and I'd argue >>> optional, piece of the puzzle. >>> >>> Related to whether or not the properties of an object may be mutable >>> (arrays, other objects, etc.), they would in practice probably need >>> to blacklist resources. We ran into that issue in PSR-7, where the >>> body stream has to be mutable, because streams. Since PSR-7 is >>> based on PHP 5.3 that doesn't cause any syntactic issues, just >>> logical issues. If the classes were marked immutable explicitly, it >>> likely would be a problem since the streams cannot be immutable, >>> thus they can't be used on an immutable object. Which is... very >>> sad making. :-( >>> >>> --Larry Garfield >>> >>> -- >>> PHP Internals - PHP Runtime Development Mailing List >>> To unsubscribe, visit: http://www.php.net/unsub.php >>> >> I like your suggestion with the ‘clone’ keyword on methods. >> >> As I said before, I think it’s a mis-step to prevent manual cloning >> at the engine level (even if the result is that you return the same >> instance) - there is *bound* to be user land code that clones >> objects. Without this, any use of clone in a distributed >> library/framework will have to do a check to see if an object (except >> those accepting final classes of known definition) is immutable >> before attempting to clone it. > > I think most people in the thread have agreed that blocking clone() is > unnecessary, the RFC just hasn't been updated yet. With a clone > method, an external clone becomes basically a pointless but harmless > operation, I'd think. A clone within the class would be fairly > nonsensical, except for the clone methods. I agree that blocking clone seems unnecessary. I also don't see why it is useful to have "clone" methods. Why not let "clone $this" produce a copy that is open to modification within the scope it's cloned in ? or, why would you limit yourself to one clone per method call ? maybe there is something I'm missing but I don't get why introducing a new method annotation is useful. it looks like an implementation details (using clone instead of new) of the method is leaking class Natural { private $n; public function __construct(int $n) {assert($n >= 0); $this->n = $n;} public function nextTen() { for ($i = 0, $c = $this; $i < 10; $i++, $c = clone $c;) { $c->n++; yield $c; } } } > >> Regarding identity, I’m going to refer back to the DateTimeImmutable >> class. I know its not the same implementation, but honestly I don’t >> think that matters. Developers use PHP because they generally *don’t* >> have to worry about the internal details of the engine. >> >> e.g.: >> $d = new DateTimeImmutable(); >> $e = $d->add(new DateInterval('PT0S')); >> >> var_dump($d === $e); // bool(false) >> >> >> Personally I think if we want to worry about whether two objects >> represent the same value, wouldn’t that be better handled (and with >> much greater positive effect for developers) by finalising & passing >> the Comparable RFC (https://wiki.php.net/rfc/comparable)? >> >> Cheers >> >> Stephen > > That does seem like the more targeted solution, yes. > > I forgot to mention before, there was discussion of marking interfaces > as immutable. I would argue yes, they should, because you'd want to > mark methods within the class as clone methods. While I suppose that > would technically not require an immutable class/interface, it's the > obvious place to use it. again, why mark method as clone method in an interface, if the interface is already marked as immutable isn't it enough to specify the return type "static" ? > > (Which is another interesting question: is there a reason to not allow > clone methods on non-immutable classes? That seems... I suppose > unnecessary but harmless to allow, so may as well?) > > --Larry Garfield > -- Mathieu Rochette