Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:113056 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 20389 invoked from network); 3 Feb 2021 14:31:02 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 3 Feb 2021 14:31:02 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 0B8EF1804CF for ; Wed, 3 Feb 2021 06:14:31 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-lj1-f172.google.com (mail-lj1-f172.google.com [209.85.208.172]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 3 Feb 2021 06:14:30 -0800 (PST) Received: by mail-lj1-f172.google.com with SMTP id f2so28425702ljp.11 for ; Wed, 03 Feb 2021 06:14:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=K9C+5cduMMEjXgmEMAJwLyIwFRHb/A79bgVSJ3R+7Nk=; b=MVRiYchqcV0x8WFZK7A/OpVtPd5oO69TYvkEhpq3ENQ0iJ4oKGmYNBkid0AI01WyJl 4N2DqkABwXH4wO4YXwNf4JaiAaUISxRdugfC1ZiJ+wTE3GAth/rF4evXlofe9SzWkit4 0rBBT0pgkrYgdT8kI+pGxufLXh8/3S7rMi9BxIlIJUYYse8YcEEAy5U9e9tBT1KEMm5g UQdzrkvGSxkAa5LeytFL2GIfUvf4nn+jXP+rD3713pKD7lp5z7ZS0GBKfIPfF9gW/s1E rkh9e4T4hIaqqD8FqHg1nmwyrlzgq9rgPbnjWRjY3Ebd4xWWP8eJb4l+KcXQmqgLX9Ro xpfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=K9C+5cduMMEjXgmEMAJwLyIwFRHb/A79bgVSJ3R+7Nk=; b=rSdcwV95jtsiah/5PwfmX2GivIs0qepUXuuTEPwQtGvgxY0opKy5mLaCVLiIIwVHwk q0EbQhe9yvBBP8/zdu1KYQIYuez/Pfvb2pvzHGlP/1cvbXy6iQKiKy8QKCMBOG0yaaAA THGiaHBukCsgYBFsbX/HGn0ZAQAZYxLmAKokmiImpu0MgzR8+TLvk1jPbBQdb4PZVsyE 0/lg/8EO2tHpcTtPxDDPn85QjZoYTx1v2ZJAfPL1J+9TejvVn8DFNNo1eGSvb5NR7ilx cpMxwyyPqRq6+89LJiYlb4oLJBviAjk1KCNIs0Sn+0WCXaIZqp2k1dbu5iuo+MREptVH /GNA== X-Gm-Message-State: AOAM5303rtJ1Q8s32QYnidG7ra8N8HaM+nOwabL2Ir7WoGaD/dzqGp/a tdw+6NM2scU3DwABO3/R61L+pDCGuP333o7HiNDujox4/KynNA== X-Google-Smtp-Source: ABdhPJxwHR4Bo0VCjeLx6dghch0+3rj9XCbsXXxA+qnrpaKZb07/kgyeG+WmD9txVUHG0eh5K4rOeIXPi4e5yGBIdlc= X-Received: by 2002:a05:651c:14a:: with SMTP id c10mr1850980ljd.272.1612361666612; Wed, 03 Feb 2021 06:14:26 -0800 (PST) MIME-Version: 1.0 References: <1d0abb04-4987-43a9-85bc-bccc3bd6be9a@www.fastmail.com> In-Reply-To: <1d0abb04-4987-43a9-85bc-bccc3bd6be9a@www.fastmail.com> Date: Wed, 3 Feb 2021 15:14:10 +0100 Message-ID: To: Larry Garfield Cc: php internals Content-Type: multipart/alternative; boundary="00000000000063517305ba6f343b" Subject: Re: [PHP-DEV] Analysis of property visibility, immutability, and cloning proposals From: nikita.ppv@gmail.com (Nikita Popov) --00000000000063517305ba6f343b Content-Type: text/plain; charset="UTF-8" On Mon, Dec 28, 2020 at 9:24 PM Larry Garfield wrote: > There's been a number of discussions of late around property visibility > and how to make objects more immutable. Since it seems to have been > well-received in the past, I decided to do a complete analysis and context > of the various things that have been floated about recently. > > The full writeup is here: > > https://peakd.com/hive-168588/@crell/object-properties-and-immutability > > I hope it proves stimulating, at least of discussion and not naps. > Thanks for the analysis Larry! I want to add a couple of thoughts from my side. First of all, I think it's pretty clear that "asymmetric visibility" is the approach that gives us most of what we want for the least amount of effort. Asymmetric visibility has clear semantics, is (presumably) trivial to implement, and gives immutability guarantees that are "good enough" for most practical purposes. It's the pragmatic choice, and PHP is all about pragmatism... That said, I don't think that asymmetric visibility is the correct solution to this problem space -- I don't think asymmetric visibility is ever (or only very rarely) what we actually want, it's just a good enough approximation. Unfortunately, the alternatives are more complex, and we have a limited budget on complexity. Here are the pieces that I think would make up a proper solution to this space: 1. initonly properties. This is in the sense of the previous "write once properties" proposal, though initonly is certainly the better name for the concept. Initonly properties represent complete immutability both inside and outside the class, and I do believe that this is the most common form of immutability needed (if it is needed at all). Of course, as you correctly point out, initonly properties are incompatible with wither patterns that rely on clone-then-modify implementations. I think that ultimately, the "wither pattern" is an artifact of the fact that PHP only supports objects with by-handle semantics. The "wither pattern" emulates objects with by-value semantics, in a way that is verbose and inefficient. I do want to point out that your presentation of copy-on-write when it comes to withers is not entirely correct: When you clone an object, this will always result in a full copy of the object, including all its properties. If you call a sequence of 5 wither methods, then this will create five objects and perform a copy of all properties every time. There is really no copy-on-write involved here, apart from the fact that property values (though not the property storage) can still be shared. 2. This brings us to: Objects with by-value semantics. This was discussed in the thread, but I felt like it was dismissed a bit prematurely. Ultimately, by-value semantics for objects is what withers are emulating. PSR-7 isn't "immutable", it's "mutable by-value". "Immutable + withers" is just a clumsy way to emulate that. If by-value objects were supported, then there would be no need for wither methods, and the "clone-then-modify" incompatibility of initonce properties would not be a problem in practice. You just write $request->method = 'POST' and this will either efficiently modify the request in-place (if you own it) or clone it and then modify it (if it is shared). Another area where by-value objects are useful are data structures. PHP's by-value array type is probably one of those few instances where PHP got something right in a major way, that many other languages got wrong. But arrays have their own issues, in particular in how they try to service both lists and dictionaries at the same time, and fail where those intersect (dictionaries with integer keys or numeric string keys). People regularly suggest that we should be adding dedicated vector and dictionary objects, and one of the issues with that is that the resulting objects would follow the usual by-handle semantics, and would not serve as a mostly drop-in replacement for arrays. It is notable that while HHVM/Hack initially had vec and dict object types, they later created dedicated by-value types for these instead. 3. Property accessors, or specifically for your PSR-7 examples, guards. The __clone related issues you're mostly dealing with in your examples are there because you need to replicate the validation logic in multiple places. If instead you could write something like public string $method { guard($version) { if (!in_array($version, ['1.1', '1.0', '2.0'])) throw new InvalidArgumentException; } } then this would ensure consistent enforcement of the property invariants regardless of how it is set. Circling back, while I think that a combination of these features would be the "proper" solution to the problem, they also add quite a bit of complexity. Despite what I say above, I'm very much not convinced that adding support for by-value objects is a good idea, due to the confusion that two different object semantics could cause, especially if writing operations on them are not syntactically distinct. I've written up an initial draft for property accessors at https://wiki.php.net/rfc/property_accessors, but once again I get the distinct impression that this is adding a lot of language complexity, that is possibly not justified (and it will be more complex once inheritance is fully considered). Overall, I'm still completely unsure what we should be doing :) Regards, Nikita --00000000000063517305ba6f343b--