Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:110034 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 32974 invoked from network); 6 May 2020 09:56:45 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 6 May 2020 09:56:45 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id EF6271804C3 for ; Wed, 6 May 2020 01:31:52 -0700 (PDT) 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 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-lf1-f47.google.com (mail-lf1-f47.google.com [209.85.167.47]) (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, 6 May 2020 01:31:52 -0700 (PDT) Received: by mail-lf1-f47.google.com with SMTP id t11so607137lfe.4 for ; Wed, 06 May 2020 01:31:52 -0700 (PDT) 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=8eD1wvoAoPTbG9vxSRu99jWDvZ78isRbXtQlRp+kJzU=; b=vRud6/6hqkCnNNeI2BWSjl4OAOsjSAq+Uwxrnnp5d5/eKWhUmFUbcSgdNixyI4kK/k WK3TXOcdOmK7zw5jLZV0B5YtNL1HqcfymE+E2/fHpAiCTD4Vg1ElsO9DdMcJzqF83jfk +CewVuh5z2xXlaPLy2EaSZJr5P4GOrdT1aB5H0lWKCgRQ/3gk2MBCt3wNRmrs32nG1g+ ERW/dfjPaGYFw4cH4/ZW72r0xZy/ul5lziDmZ5dEpTVLQ5T7V7bsEB+m0EuZO1K4pTJ7 u0oxrGHbSF8ssBOA/VZ5Qle3cwJrUkYmC+G2UeE8eBzEZ8lQK6QmZgmxA8N037p9chhJ u7Dg== 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=8eD1wvoAoPTbG9vxSRu99jWDvZ78isRbXtQlRp+kJzU=; b=X+F5PYMgY0w02hpd+NcGiSL4VOle19eltuHWjRAuRRgHE0OOBSO1ZQPNUmqOvMLKZm uoF52wN3+fYDkD74VsdXg01+GIKot7iGMdFiFVhAKxnc+PqTeIDUIfgdF6S11EvsDLS1 JE8Lag70FD4MaU1dbb4CYvFW68NjM4w/1N2LUl4oSIwuGUm9+q+6ItXwEIz4qt2rOsuq LvPTX3kEsVVHuZYjtP2QFKurV4ta91DLqVOIJk3h3rg084FjgOaLwd/9d0XQ0p9m62H5 THeqlMKqMJtdsrICrjA22xr0cMG+q0YdGXUBGtWogg3Jn+/B2U4Cdn981Ym0qCS6qq1c gkMw== X-Gm-Message-State: AGi0Pub6oVm6a1WU2upLLT3Tplhkj+f1lGWzz8pfI2nCwNM277PN1dBl OzHUTd6YCY7MRn07LIUSQFqhZ9Hmn4SGFwOkMmTTz1sctY8= X-Google-Smtp-Source: APiQypLjfmRD7j2JIt31CJcXj0i9VzxEV98FQ5AKkEc3qEqMps0Wlxf1sh6FbYxYXlz9xkQKLzJhnATPnSUOrURCqH4= X-Received: by 2002:ac2:4c83:: with SMTP id d3mr4458029lfl.92.1588753910751; Wed, 06 May 2020 01:31:50 -0700 (PDT) MIME-Version: 1.0 References: <76b72148-4b3a-4a63-ab4c-245439a94823@www.fastmail.com> <8dc53b46-8156-4b62-a521-d6d6fd379281@www.fastmail.com> In-Reply-To: <8dc53b46-8156-4b62-a521-d6d6fd379281@www.fastmail.com> Date: Wed, 6 May 2020 10:31:34 +0200 Message-ID: To: Larry Garfield Cc: php internals Content-Type: multipart/alternative; boundary="0000000000007c793405a4f698a6" Subject: Re: [PHP-DEV] Re: [RFC] Constructor Property Promotion From: nikita.ppv@gmail.com (Nikita Popov) --0000000000007c793405a4f698a6 Content-Type: text/plain; charset="UTF-8" On Tue, May 5, 2020 at 10:27 PM Larry Garfield wrote: > On Tue, May 5, 2020, at 7:35 AM, Nikita Popov wrote: > > > > Performing validation when the getAttributes() call is performed does > sound > > reasonable to me. We can also add a class flag to perform this validation > > only once (if it is successful), so the cost doesn't have to be paid by > > every single getAttributes() consumer. > > > > For the purpose of this RFC, I've now updated it to say that attributes > > will be applied to both properties and parameters ( > > https://wiki.php.net/rfc/constructor_promotion#attributes), but with an > > explicit note that we should change the behavior prior to the PHP 8 > release > > if it turns out to be problematic, e.g. with regards to an attribute > > validation feature. I think this is something of a detail, and we'll be > > mostly fine whichever way we chose, but it's hard to say right now which > > option is best, in particular with regard to future attributes > improvements. > > > > Regards, > > Nikita > > Thinking on it further, there's something sitting in the back of my head > that's bothering me. This helped me flesh it out. > > I like promotion, and think it will be great for simplifying code. > However, as attributes show as we load more bells and whistles onto > properties (even if they are desirable and useful bells and whistles) the > level of conflict is going to get larger. > > Consider, today we have: > > class Point { > protected int $x; > protected int $y; > > public function __construct(int $x, int $y) { > $this->x = $x; > $this->y = $y; > } > } > > And this is needlessly verbose. With promotion, that simplifies to: > > class Point { > public function __construct(protected int $x, protected int $y) {} > } > > And this is good. There seems little dispute about that. > > But then we add attributes, and it starts to get a little odd: > > class Point { > public function __construct( > <> > protected int $x, > > <> > protected int $y, > ) {} > } > > And has the possible confusion between parameter attributes and property > attributes. > > But... there's plenty of other things that may get added to properties. > We've discussed the "readonly" property and the more robust assymetric > visibility. What would that look like? > > class Point { > public function __construct( > <> > int $x { > public get, > private set, > }, > > <> > int $y { > public get, > private set, > }, > ) {} > } > > And that's now an awful lot of metadata to shove into, technically, a > function signature. And if accessor methods ever happen: > > class Point { > public function __construct( > <> > int $x { > public get () { > return $this->x; > }, > private set($new) { > if ($new < 0) { > throw new Exception(); > } > $this->x = $new; > }, > }, > > <> > int $y { > public get () { > return $this->y; > }, > private set($new) { > if ($new < 0) { > throw new Exception(); > } > $this->y = $new; > }, > }, > ) {} > } > > And now we have methods defined inside a function signature, and that's > just bizzarro. It also means at least 4 indent levels before you even get > to the body of the methods. > > And there's probably other things that could get attached to properties at > some point I'm not thinking of that someone will suggest. > > So I have to wonder, are we painting ourselves into a hole? Most of the > uses I can think of for extra-metadata-properties are... overlapping with > the case where you want to have constructor promotion. That means "well if > you need to do fancy stuff just don't use promotion" becomes a > non-sustainable answer, because the cases where you want both will increase > rather than be an edge case. > > Is there a way we can build in an escape hatch for ourselves to avoid > these issues, both attributes and for future extension? > > First idea off my head, which may or may not be good: > > Allow an alternate keyword to fill in the body of the constructor, but NOT > fill in the property. You still define the property yourself and put all > the extra metadata there. To wit: > > class Point { > <> > int $x { > public get () { > return $this->x; > }, > private set($new) { > if ($new < 0) { > throw new Exception(); > } > $this->x = $new; > }, > }, > > <> > int $y { > public get () { > return $this->y; > }, > private set($new) { > if ($new < 0) { > throw new Exception(); > } > $this->y = $new; > }, > }, > > public function __construct(promote int $x, promote int $y) {} > } > > That eliminates 2 of the 4 places you'd have to repeat the name instead of > 3, but means the constructor isn't burdened with having to hold a > potentially non-trivial amount of metadata. It also resolves the attribute > question as splitting it up like this would let you do "one or the other" > (property vs parameter). > > That may not be the best way, but I'm slightly uneasy with a path that > could lead to 30 lines of property metadata squeezed into a constructor > signature definition. :-) > Hey Larry, At least for me, there's a pretty clear line on what is acceptable to use with constructor promotion and what isn't: That line has been crossed when your property declarations started to include their own {} blocks. Constructor promotion is a short-hand notation for the cases where the constructor boilerplate is much larger than the actual functionality. If you have 100 lines of property declarations due to heavy accessor use, then I think the value proposition of constructor promotion converges to zero, because the constructor boilerplate is no longer a dominating factor. Regarding your last example, something similar to this has already been proposed (and declined) in a slightly different form in https://wiki.php.net/rfc/automatic_property_initialization: class Point { public int $x { ... }; public int $y { ... }; public function __construct(int $this->x, int $this->y) {} } Regards, Nikita --0000000000007c793405a4f698a6--