Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:81508 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 90585 invoked from network); 1 Feb 2015 00:42:19 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 1 Feb 2015 00:42:19 -0000 Authentication-Results: pb1.pair.com header.from=larry@garfieldtech.com; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=larry@garfieldtech.com; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain garfieldtech.com from 66.111.4.27 cause and error) X-PHP-List-Original-Sender: larry@garfieldtech.com X-Host-Fingerprint: 66.111.4.27 out3-smtp.messagingengine.com Received: from [66.111.4.27] ([66.111.4.27:60239] helo=out3-smtp.messagingengine.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 36/44-01632-A667DC45 for ; Sat, 31 Jan 2015 19:42:18 -0500 Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailout.nyi.internal (Postfix) with ESMTP id D80C9208CD for ; Sat, 31 Jan 2015 19:42:15 -0500 (EST) Received: from frontend2 ([10.202.2.161]) by compute1.internal (MEProxy); Sat, 31 Jan 2015 19:42:15 -0500 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=x-sasl-enc:message-id:date:from :mime-version:to:subject:references:in-reply-to:content-type :content-transfer-encoding; s=smtpout; bh=5K1E1nAVpvWkXTrv842PWQ WsXU0=; b=O0kEwmLucyzW6dwCQ22BuHziyA6XbYogGH1HyCRb9duhS7yyTlJi0I ntWLxsLWlI7P7VDVFlMmb04BbfFy6t9/Ccfc7f5Xaa3V3iBW4r0JoC+DGrTJ+feg tgHjcGILYOLuvMfRsTvoWNZwZor7K76bTKoZ7fZkqybzADqFHp0wg= X-Sasl-enc: 99PvIgxFPG6F+uHfnrA2+9G/8577OAu0oHqsufoZwnDC 1422751335 Received: from [192.168.42.108] (unknown [98.226.241.18]) by mail.messagingengine.com (Postfix) with ESMTPA id 92FBF68009E for ; Sat, 31 Jan 2015 19:42:15 -0500 (EST) Message-ID: <54CD7668.30301@garfieldtech.com> Date: Sat, 31 Jan 2015 18:42:16 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 MIME-Version: 1.0 To: internals@lists.php.net References: <54CBC804.7050706@gmail.com> In-Reply-To: <54CBC804.7050706@gmail.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] [RFC] Immutable variables and objects From: larry@garfieldtech.com (Larry Garfield) On 01/30/2015 12:05 PM, Rowan Collins wrote: > 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. The with*() methods in PSR-7 are documented to return a new instance, not modify the existing instance. Yes, there's no way in PHP itself to force that syntactically, which is why documentation exists. :-) Also, in the benchmarks we've run the performance cost of all those new objects is measured in nanoseconds, ie, small enough that we're not worried about it. (Hats off to the PHP Internals folks for making that fast!) > >> 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. *snip* Immutability, generally, offers two advantages: 1) It makes it easier for humans to reason about code. 2) It makes it easier for compilers/runtimes to reason about code. For the former, good programming practices/standards can often suffice as there are cases where immutability makes code uglier, not better. For the latter, it allows the compiler/runtime to do two things: Catch code errors early and optimize based on assumptions. In practice, I don't see much value in a language allowing a variable to be flagged as mutable or immutable *unless* the default is immutable, as in F# or Rust. In those cases it encourages the developer to take more functional, immutable approaches most of the time, which (it is argued) lead to better code, and more optimizable code (because the compiler can make more assumptions). Switching PHP to default-immutable variables is clearly off the table, so allowing individual variables to be explicitly marked as immutable, particularly scalars as Stanislav points out, doesn't sound like it would offer much. What *could* be valuable, however, is flagging *parameters*. Ie: function foo(const MyClass $c, const $s) { $s = 'abc'; // Compiler error $c = new MyClass(); // Compiler error. $c->foo = 'abc'; // Some kind of error? } With PHP's existing copy-on-write there's not much memory-savings to be had with "const reference" parameters, as in C. What the above would allow is for the compiler to catch certain errors, especially if the parameters are on a method in an interface. Ie, the following: interface Foo { public function setFromThing(const Thing $t); } Makes it explicitly clear that an implementer is *not allowed* to modify $t in their setFromThing() implementation. Of course, as Rowan notes object encapsulation makes enforcing that quite difficult, which is arguably by design. So I suppose the more general question is, are there code annotations (immutable values, marking a function as pure, explicit scalar types, etc.) that developers could insert that would: 1) Provide guidance for developers reasoning about the code. 2) Allow the compiler to catch more bugs. 3) Allow the compiler to better optimize the code by safely making more assumptions. 4) Some combination of the above. Eg, if the compiler knows a given function is pure (explicit input and output, no side-effects, immutable of the parameters) then it could auto-memoize it, or inline safely (eliminating the function call entirely), or other such things. Is there a way that a developer could syntactically tell the compiler "you can safely make these assumptions and optimize/error-check accordingly"? And could Zend Engine support such optimizations in a meaningful way? (I have no idea on the latter.) --Larry Garfield