Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:81442 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 95201 invoked from network); 30 Jan 2015 18:06:23 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 30 Jan 2015 18:06:23 -0000 Authentication-Results: pb1.pair.com header.from=rowan.collins@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=rowan.collins@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 74.125.82.171 as permitted sender) X-PHP-List-Original-Sender: rowan.collins@gmail.com X-Host-Fingerprint: 74.125.82.171 mail-we0-f171.google.com Received: from [74.125.82.171] ([74.125.82.171:53195] helo=mail-we0-f171.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id C9/4B-35409-E18CBC45 for ; Fri, 30 Jan 2015 13:06:22 -0500 Received: by mail-we0-f171.google.com with SMTP id k11so26179927wes.2 for ; Fri, 30 Jan 2015 10:06:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:subject:references :in-reply-to:content-type:content-transfer-encoding; bh=zGWk7uzmwrXmDdgMahFADzhuIQk1/mEV3/x7YwRkYGA=; b=rtpIK4HCXYf7xpeiioi+NqagIPb1TbF5IICoyYDs6z2yn4pDjszmMjEDKhxc0q62dM 1yInGuONXdT5yk5GsML/6bl1lMWuiOhOttQkF+8fOlf0aY5n6XcSj0bNCq4ai02lvjV/ XlUR+17PwGlVdjLuOxLCrIVuHZ9JoPkF+NaYBUFrDTh6xGc6aIGYrMNMzJqjoE3jkLQG aIECFml99PSi9VEV+id0ONKps9P8yF9zHKDTYGcOUaFwIkW+YXGaW6HnMk+CoeoHzG0k 09NQVeGr9z6cJrJ40unKjg9PUvjL+cwsmQPb4gLUYpDx+J+f+85DkOqVUmyUccGej8jH +bgQ== X-Received: by 10.194.184.41 with SMTP id er9mr14580303wjc.23.1422641179490; Fri, 30 Jan 2015 10:06:19 -0800 (PST) Received: from [192.168.0.2] (cpc68956-brig15-2-0-cust215.3-3.cable.virginm.net. [82.6.24.216]) by mx.google.com with ESMTPSA id l6sm15936509wjx.33.2015.01.30.10.06.18 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 30 Jan 2015 10:06:18 -0800 (PST) Message-ID: <54CBC804.7050706@gmail.com> Date: Fri, 30 Jan 2015 18:05:56 +0000 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 MIME-Version: 1.0 To: internals@lists.php.net References: In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] [RFC] Immutable variables and objects From: rowan.collins@gmail.com (Rowan Collins) On 30/01/2015 13:07, Alexander Lisachenko wrote: > What is wrong here? Emulated immutability. All methods will create a > separate instance of request, so > $baseRequest->withUri()->withMethod()->withHeader()->withBody() will create > total 5 object instances. It's a memory overhead and time consumption for > each method call. Like Andrea, I don't see how immutable variables/objects solve this problem. The overhead is not from emulating the immutability, it is a consequence of the design pattern choosing immutability. In fact, from the code shown, there is no way to know that immutability is in any way relevant, since the definition of withUri() could be a mutator which ends "return $this", a pattern I've frequently used to create such "fluent" interfaces. Copy-on-write doesn't help, either, since all 5 calls are writes, so will still make 5 copies. > What I want to discuss is true immutability flag for variables and > parameters. There are a lot of languages that use "final" or "const" > keywords to prevent modification of variables. On the concrete suggestion, though, I do think there are possible advantages to this. I've long felt the confusion people have over pass-by-value for primitives, pass-by-value-which-is-actually-a-pointer for objects, and pass-by-reference for a variable which might or might not be an object pointer, is a failure not of the programmer but of the programming language. In a blog post about it a few years ago [1], I suggested that deep immutability (along with deep cloning) could provide a better framework than by-value vs by-reference in modern OO languages. This is rather different from defining a *type* that is immutable, since it implies temporary immutability of a particular instance; but it seems to be what at least some of your examples are hinting at. The problem is that deep cloning and deep immutability are non-trivial; PHP notably doesn't support deep cloning of objects, requiring each class to define what to do with any non-primitive members, since some may represent resources which can't be meaningfully cloned just by copying data in memory. In the same way, in order to make an instance deeply immutable, you need to nail down the details of which actions are actually allowed while it's in that state, and that gets complicated: - Directly assigning to a public property is clearly invalid, as is calling a method on that property which mutates it. - If the property is itself an object, assigning it to a temporary variable must retain the immutability - that is, we don't want "$foo->bar->setValue(42);" to behave differently from "$x = $foo->bar; $x->setValue(42);" - Calling a method which internally performs such an action is also invalid (like the setValue() in the example above). This could be achieved by executing the function but marking $this as immutable, but that means that the method may have other effects up to the point where the violation occurs, so it would be preferable to somehow invalidate the method call. - For objects which represent proxies for external resources, there may be methods which mutate that external state, and properties which exist only as virtual getters. So calling $db->insertRow(...) should probably be invalid, since it changes the value of $db->lastInsertedId. I think the only way to do it would be for every method to be invalid unless it is explicitly marked as "immutable-safe", e.g. class Foo { private $delegatedObject; public function __construct() { $this->delegatedObject = new SomethingElse; } immutable public function getValue() { return $this->delegatedObject->getX(); } public function setValue($newValue) { echo "Setting!"; $this->delegatedObject->setX($newValue); } } const $foo = new Foo; echo $foo->getValue(); $foo->setValue(42); // Instantly fails without echoing "Setting!", but method is illegal in immutable context Copy-on-write, at the user object level, requires both this *and* deep cloning: $bar = clone-on-write $foo; $bar->setValue(42); // Needs to implicitly clone $foo before calling setValue(), but must also clone $delegatedObject for that to be meaningful So class Foo needs an implementation of __clone() as well before any of this can be used, and if it is missing, the resulting behaviour may be rather non-obvious. As ever, the devil's in the detail. The only language I know of that's meaningfully tackled this is Rust, with its concepts of "boxes" and "lending", although I'm hazy on the details. [1]: http://rwec.co.uk/blog/2010/08/object-references-are-confusing/ Regards, -- Rowan Collins [IMSoP]