Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:119043 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 95156 invoked from network); 27 Nov 2022 17:55:33 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 27 Nov 2022 17:55:33 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 95F57180503 for ; Sun, 27 Nov 2022 09:55:32 -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=-2.1 required=5.0 tests=BAYES_00,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, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 27 Nov 2022 09:55:29 -0800 (PST) Received: by mail-wr1-f43.google.com with SMTP id v1so13547512wrt.11 for ; Sun, 27 Nov 2022 09:55:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=3VNDL8u2m5AoQklmKWctd9nT7gYCJM2HGG1LF/HfGnQ=; b=HzJZUugIH00194Gv5hF4zykdiwjUZpQOVz/EeWy/I/pFmt2N15G7XTH3Inor1WqnDA MlPFaf46GsKmMtU/zZjK4jOTLnbXTud2HqBl2LI2MW0ZLgvbzUs54i9sRV/ssTUSdDhP KolirFebp6foml8EKDWemerFt8TlxR/m0yUpnH1+12hVY6Yhrx8FctjtvUKhWLp3+z0u 3B9SmHFIThyOyrgJFEfA4u5iaYNsKR/GfrJROjGl/FJyBaKYjeYjmZDWrD3b1OMvbjpH V+rsFkfGtqmya78X5JJYogyCDwXUB3v9Tjvaga/HwPpO/rE8hAt9ZNlOYpPF2u0EwfU1 nwcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=3VNDL8u2m5AoQklmKWctd9nT7gYCJM2HGG1LF/HfGnQ=; b=KPNxLV6S6klcwow/GHNJNiOdBA1FYsbYcM11vNRseyRcgO+a+ggS0fLXG4k1PXxQyD EGUo92f2Sk0hUgvjiekPW3ApE17Vcq7yChNzAA7m7x9qnxHpFru3d60qzj4upKF0zO0y JMaaYGTxzdcbOejMuM9Ghej0uR8084XI1zvad02+Xaqk5BGWf6gj6OldS3WzZpx8BbT3 PoB9ZQDMaah2xIGGAtO7Djihgn+Q4LISYQq2AZbs1tckLftIN/O7WkxNPMkL1VRziZTy 9az2l9d+cL1GLBquLTtTZTw5AsqKB/4dHejwERPE/YfuDSDHVr4qH7Ora+GZ2BuTRhyo Mydg== X-Gm-Message-State: ANoB5pkr+wGEvzZe7JymJL6Cc3ExBCjTbBUHTwgrZVNdj+xkDZMLIqRE N8+KlaBSjC5lgjwxtk2hw0MQg9GbuAYrvKs1FTxZE0nTbJk= X-Google-Smtp-Source: AA0mqf4wSj4qmc7ARPGyMCHq7SOPP7/vfsyCmlHnvVyzYeBgK+3OD9L3BSWjgscR5QhZZSmcjVaW938v7J74/+6cjM4= X-Received: by 2002:a5d:6b08:0:b0:236:4b06:bbb1 with SMTP id v8-20020a5d6b08000000b002364b06bbb1mr28694812wrw.303.1669571727484; Sun, 27 Nov 2022 09:55:27 -0800 (PST) MIME-Version: 1.0 References: <38e576a2-a293-41b7-990f-9d4ecfad3b70@app.fastmail.com> In-Reply-To: <38e576a2-a293-41b7-990f-9d4ecfad3b70@app.fastmail.com> Date: Sun, 27 Nov 2022 18:55:15 +0100 Message-ID: To: php internals Content-Type: multipart/alternative; boundary="000000000000be6cd705ee77757e" Subject: Re: [PHP-DEV] [RFC] [Discussion] Readonly class amendments From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000be6cd705ee77757e Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable The code that Marco (Pivetta) shared was supposed to illustrate how readonly classes can be useful to enforce some invariants on child classes. Yet, here is another implementation that does use a readonly child class, but still provides the behavior that was supposedly prevented by the keyword: readonly class MutableCounter extends ImmutableCounter { private stdClass $count; public function __construct(int $count) { $this->count =3D (object) ['count' =3D> $count]; } public function add1(): self { return new self(++$this->count->count); = } public function value(): int { return $this->count->count; } } This counterexample shows that readonly classes do not provide any extra safeguards. As it stands, the readonly keyword on classes has two consequences: 1. it forces writing dummy boilerplate to work around the current limitation while failing to provide any real guarantees 2. it gives false expectations - the exact one that we're discussions about here (no, readonly classes don't help enforce any aspects of LSP) 1. is why I call the current restriction arbitrary, and 2. might be dangerous since it would make people build on non-existing grounds. If we stay with the current way, we'll just have another WTF in the language IMHO. One that will make it harder to master PHP. Le dim. 27 nov. 2022 =C3=A0 17:51, Larry Garfield = a =C3=A9crit : > On Sat, Nov 26, 2022, at 6:35 PM, Jordan LeDoux wrote: > > On Sat, Nov 26, 2022 at 3:40 PM Deleu wrote: > > > >> > >> As I think more about this, there's nothing about the current RFC in > this > >> code sample. What's breaking LSP here is the child class doing state > >> modification, not PHP. To further expand that rationale, PHP allows u= s > to > >> create child classes. Whether that class will be LSP-safe or not is up > to > >> us, not up to PHP. > >> > >> However, the point still stands. Allowing child classes to break > readonly > >> will make it easier to build code that breaks LSP. The question then > >> becomes: why is this being proposed and is it worth it? > >> > > > > I cannot help but feel that the way `readonly` is being treated is goin= g > to > > end up one of those things that is regretted. "Readonly does not imply > > immutability". The fact that very nearly *every* single person who has > not > > worked on the RFCs has at some point been confused by this however shou= ld > > be very telling. > > > > This comes from two *different* avenues that compound with each other t= o > > *both* make this design head-scratching to me. > > > > First, in virtually all other technical contexts where the term > "readonly" > > is used, it means that the information/data cannot be altered. That is > not > > the case with readonly. In PHP, in this implementation, it is not > > "readonly" in the sense that it is used everywhere else for computing, = it > > is "assign once". > > > > Second, the English words "read only", particularly to native speakers, > > make this behavior very counterintuitive and confusing. I won't belabor > > that point further. > > > > What "read only" really is, is "constructor initialize only". It honest= ly > > has nothing to do with "read" as it's implemented. > > Not quite. It really is just write-once. The idea that you can only do > that in the constructor is not in the language; that's been invented by > over-eager static analysis tools. (Everyone should disable that check.) > > > I guess I worry that this RFC makes `readonly` even more of a minefield > for > > PHP developers, increasing the mental load of using it in code while > *even > > further* watering down the benefits it may provide. It's already design= ed > > in a somewhat counterintuitive way that I feel will be almost completel= y > > replaced in actual code in the wild by "immutable" if PHP ever gets tha= t. > > Working on asymmetric visibility, I have come to agree. Nikita proposed > readonly as a "junior version" of asymmetric visibility, to cover the mos= t > common use case without introducing more complexity. At the time, he was > confident that it wouldn't preclude expanding to asymmetric visibility in > the future. Well... I can say with confidence at this point that is not > correct, and the design of readonly is causing issues for asymmetric > visibility, and for cloning, to the point that (based on feedback in the > other thread) we're likely going to for now forbid readonly and a-viz on > the same property. > > At this point, I think I view readonly as a cautionary tale about the > costs of doing "quick and easy" design over something more robust, becaus= e > the quick-and-easy creates problems down the line that a more thoughtful, > holistic view would have avoided. > To me, the best outcome of this discussion should be to retire readonly *classes*. They were thought as a quick way to not repeat the readonly keyword on every property, but they in fact introduce this behavior + expectations related to child classes and these collide. From a conceptual ground, I think we proved that there are no guarantees to pass to child classes, but I can't argue against everybody's first understanding, even if they're wrong. Is retiring the keyword on classes an option? I feel like this ship has sailed. But then, if we can't retire them, we should at least fix them. > > LSP doesn't exist because it is some objectively better way of > programming > > according to universal laws of entropy or something. It is instead > > important because LSP helps programmers be able to predict the behavior > of > > the program they are writing and reduces the short-term memory load > > involved in programming and architecture. > > > > Something that *technically* complies with LSP but makes the program > harder > > to predict and increases the mental load of programming violates the > > *purpose* of LSP. We can argue about whether it is technically correct, > but > > I feel like that somewhat misses the point: making the language more > > capable, more stable, and more predictable. > > > > In other words, I do not believe it is that important or care to argue > > about whether this RFC violates LSP. It violates the *purpose* of LSP, > and > > that's a bigger problem to me personally. > > > > Jordan > > At this point, I am inclined to agree. readonly is wonky enough as is. > Making it semi-LSP (in spirit) just makes it even more wonky. To flip th= e > earlier sentiment, "readonly is broken enough as is, let's not break it > even further." > I'm not comfortable with this sort of "discrediting" of readonly as it works now. At the time, I thought asymmetric visibility was the thing we needed. Yet the community decided that we needed the "assign once" extra guarantee. It's not really fair to now call this as "broken". It's not. It works as designed, and to my experience, it is a useful feature, especially for public/protected properties. I don't see much discussion about the cloning part of the RFC. Marco wrote "allowing mutation in `__clone()` is fine" and I feel like that's also fine to most, am I correct? That's the most critical part of the proposal, since as I just explained, the first part of the RFC is a behavior that we can work around already (but that makes the language a maze =C2=AF\_(=E3=83=84)= _/=C2=AF.) Nicolas --000000000000be6cd705ee77757e--