Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122503 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 89BCE1AD8F6 for ; Mon, 26 Feb 2024 19:02:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1708974165; bh=gfCpq4PcnmBCYZlSMO2A9iTHhRVPk1fcMEV57ONmPAQ=; h=Date:From:Subject:To:References:In-Reply-To:From; b=D37G11HdX1i2EEH9Jkmk6HZWare2cSF5Rfp3jqK+P9SwGVoLtT/t1Cf3fWotNFECO nmyqFHQBUQ3/IkL4dyy07hbkkUtJYOPrkiCEYqzTRO2KcOWqQ+/bHIgkjFjZccYHOU wFVzQ7sG40XMPT/SHfpypqYLRPMudceAWWQDI5rMb4Amc7UBY/LlUXN/zaAMmuJQL5 KRGFGS4sGOQVBx/k2GOwLGnVSjKc5FZ5pMMne/z0Zg3rGK8VcQojDZoMu04E/FTw5q tbj82aO7iyTPG/5kzvu5KpGNAU/Era2DZVLwZrP4yow+GeznsrVLYhklKzGGF69R6Q n92RR8eBi/K2w== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3D8FC180082 for ; Mon, 26 Feb 2024 19:02:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-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,DMARC_PASS,HTML_MESSAGE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mta3.mail.genkgo.net (mta3.mail.genkgo.net [2.58.165.21]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 26 Feb 2024 11:02:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=genkgo.nl; s=ge-k1; h=In-Reply-To:References:To:Subject:From:MIME-Version:Date: Message-ID:Content-Type:Sender:Reply-To:Cc:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=e3zn61qjw0VF+n0SpreHoTkOwGrmrn27WgSXOfDAyf8=; b=aCkRLYctrjb7XfYJAVRe4X2bYz OhSpazye4uC3ZZ3J7WMeTeXQTIyuTJEM40xYMRL7UcBuaehlyeGwkcqSpbhQRMK1Zbkqhrd1fGiR7 OIam1UDYVO9SPBCfX6jnfrR3bibuMqLoFNIPZ6ATa7YMevtvv95teqsFFaK56pprPn5n1P+pbBtdV tWD6KTJC3IMif18CX76nLuP3LbaK9UTmpMF2SInLZUrI9jZCBgXdvAHNDpXE8IysverCP/TjKmmj7 UHz68/22Gw9goulspDZohqWRBcUj22eS+WjaPLrctK8v39e4xtSMuPh/5ZK74TomJ8WB2OtFlTRat vXbBOUQw==; Received: from [185.184.111.39] (helo=[192.168.16.250]) by mta3.mail.genkgo.net with esmtpsa (TLS1.3) tls TLS_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1regFI-0003Lo-0K for internals@lists.php.net; Mon, 26 Feb 2024 19:02:36 +0000 Content-Type: multipart/alternative; boundary="------------3eXaWoLJsKTlv7Y5fHbUBn0i" Message-ID: <2fdf1933-b51c-40cc-8d02-31899b96c71c@genkgo.nl> Date: Mon, 26 Feb 2024 20:02:34 +0100 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2 To: internals@lists.php.net References: <790b5b4e-f51b-4050-a12a-5fa903d0568f@app.fastmail.com> <52C6F501-8E23-42D7-8541-88A22AD79375@koalephant.com> <36e90d8d-d275-4ce9-9dd9-1e2422c6d3a9@app.fastmail.com> Content-Language: en-US, nl-NL In-Reply-To: <36e90d8d-d275-4ce9-9dd9-1e2422c6d3a9@app.fastmail.com> From: f.bosch@genkgo.nl (Frederik Bosch) This is a multi-part message in MIME format. --------------3eXaWoLJsKTlv7Y5fHbUBn0i Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 23-02-2024 16:58, Larry Garfield wrote: > On Fri, Feb 23, 2024, at 8:33 AM, Stephen Reay wrote: >>> On 23 Feb 2024, at 06:56, Larry Garfield wrote: >> Hi Larry, >> >> It's good to see this idea still progressing. >> >> >> I have to agree with the other comment(s) that the implicit >> `$field`/`$value` variables seem odd to me. I understand the desire for >> brevity and the potential future scope of reused hooks, but this >> concept seems to fly in the face of many years of PHP reducing "magic" >> like this. > The "magic" that PHP has been removing is mostly weird and illogical type casting. As noted, neither of these variables are any more "magic" than $this. > > However, since it seems no one likes $field, we have removed it from the RFC. Of note, to respond to your comment further down, $this->{__PROPERTY__} will not work. The virtual-property detection looks for the AST representation of $this->propName, and only that. Dynamic versions that turn into that at runtime cannot work, as it needs to be known at compile time. > > For $value, however, we feel strongly that having the default there is a necessary part of the ergonomic picture. In particular, given your comments here: > >> To give one answer to your question about ambiguity if the `$value` >> parameter is required - I don't believe this is actually ambiguous, in >> the context of PHP: >> - method parameters in child classes don't implicitly 'inherit' the >> parent method parameter's type if they don't define one (they widen to >> mixed); >> - method return types have no implicit inheritance, they must declare a >> compatible return type; >> - typed class properties don't implicitly inherit the parent type when >> the type left off a child property - they must declare the same type. >> >> AFAIK there is no existing behaviour in PHP where omitting a type would >> mean "the type is implicitly inherited from X", it either means the >> same as mixed, or it's an error. > That to me suggests that IF a custom variable name is provided, we should require also specifying the type. In which case, in the 95% case, if we require the full argument signature then the 95% case would need to double-specify the type, which is a hard-no from an ergonomic standpoint. > > Especially combined with the suggestion yesterday to allow return-to-set in the short-set version, that would mean comparing this: > > public string $phone { > set(string $phone) => $this->phone = $this->sanitizePhone($phone); > } > > To this: > > public string $phone { > set => $this->sanitizePhone($value); > } > > And to me, there's absolutely no contest. The latter has about 1/3 as many places for me to make a typo repeating the same information over again. Now imagine comparing the above in a property that's used with constructor promotion. > > public function __construct( > public string $phone { set(string $phone) => $this->phone = $this->sanitizePhone($phone); } > public string $phone { set => $this->sanitizePhone($value); } > ) {} > > Again, it's absolutely no contest for me. I would detest writing the longer version every time. > > If PHP has been moving away from weird and inexplicable magic, it's also been moving away from needless boilerplate. (Constructor promotion being the best example, but not the only; types themselves are a factor here, as are arrow functions.) As the whole point of this RFC is to make writing common code easier, requiring redundant boilerplate for it to work is actively counter-productive. > > So what I'd suggest instead is "specify the full signature if you want a custom name OR wider type; or omit the param list entirely to get a same-type $value variable, which 99% of the time is all you need." We get that in return for documenting "$value is the default", which for someone who has already figured out $this, should be a very low effort to learn. > >> Also, a small nitpick: The link to your attributeutils repo in the >> examples page, is broken, and it would be nice to see a few examples >> showing the explicit version of the hooks. > Link fixed, thanks. What do you mean explicit version of the hooks? > > --Larry Garfield Hi Larry, Great to see you pick up on this proposal again. Rather than "just use $this->propName directly", you might want to consider yield to commit the property value, as a solution to "to assign and confirm the property write while still allowing code to run after it". But then return would be also be a logical consequence. public string$propName { set($value) { yield $value; $this->doSomething(); yield $value; return $this->doSomethingElse(); // final commit, not required } } If I read your remarks on why not using return I do not get why this proposal wants to deviate from a normal function syntax. function test(string $name) {     return [$first, $last] = explode(' ', $name); } var_dump(test("Larry Garfield")); // returns ["Larry", "Garfield"] That's how it always has been, no? So in your example, short code abbreviated form would not work. One has to write a block. public string$fullName { set=> [$this->first, $this->last] = explode (' ', \ucfirst ($value)); // error, $fullName is a string, returning array } public string$fullName { set{ [$this->first, $this->last] = explode (' ', \ucfirst ($value)); // no error, not returning } } Hope this RFC will develop into something that will pass. Good luck! Regards, Frederik --------------3eXaWoLJsKTlv7Y5fHbUBn0i Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit


On 23-02-2024 16:58, Larry Garfield wrote:
On Fri, Feb 23, 2024, at 8:33 AM, Stephen Reay wrote:
On 23 Feb 2024, at 06:56, Larry Garfield <larry@garfieldtech.com> wrote:
Hi Larry,

It's good to see this idea still progressing.


I have to agree with the other comment(s) that the implicit 
`$field`/`$value` variables seem odd to me. I understand the desire for 
brevity and the potential future scope of reused hooks, but this 
concept seems to fly in the face of many years of PHP reducing "magic" 
like this.
The "magic" that PHP has been removing is mostly weird and illogical type casting.  As noted, neither of these variables are any more "magic" than $this.

However, since it seems no one likes $field, we have removed it from the RFC.  Of note, to respond to your comment further down, $this->{__PROPERTY__} will not work.  The virtual-property detection looks for the AST representation of $this->propName, and only that.  Dynamic versions that turn into that at runtime cannot work, as it needs to be known at compile time.

For $value, however, we feel strongly that having the default there is a necessary part of the ergonomic picture.  In particular, given your comments here:

To give one answer to your question about ambiguity if the `$value` 
parameter is required - I don't believe this is actually ambiguous, in 
the context of PHP:
- method parameters in child classes don't implicitly 'inherit' the 
parent method parameter's type if they don't define one (they widen to 
mixed);
- method return types have no implicit inheritance, they must declare a 
compatible return type;
- typed class properties don't implicitly inherit the parent type when 
the type left off a child property - they must declare the same type.

AFAIK there is no existing behaviour in PHP where omitting a type would 
mean "the type is implicitly inherited from X", it either means the 
same as mixed, or it's an error.
That to me suggests that IF a custom variable name is provided, we should require also specifying the type.  In which case, in the 95% case, if we require the full argument signature then the 95% case would need to double-specify the type, which is a hard-no from an ergonomic standpoint.

Especially combined with the suggestion yesterday to allow return-to-set in the short-set version, that would mean comparing this:

public string $phone {
    set(string $phone) => $this->phone = $this->sanitizePhone($phone);
}

To this:

public string $phone {
    set => $this->sanitizePhone($value);
}

And to me, there's absolutely no contest.  The latter has about 1/3 as many places for me to make a typo repeating the same information over again.  Now imagine comparing the above in a property that's used with constructor promotion.  

public function __construct(
    public string $phone { set(string $phone) => $this->phone = $this->sanitizePhone($phone); }
    public string $phone { set => $this->sanitizePhone($value); }
) {}

Again, it's absolutely no contest for me.  I would detest writing the longer version every time.

If PHP has been moving away from weird and inexplicable magic, it's also been moving away from needless boilerplate.  (Constructor promotion being the best example, but not the only; types themselves are a factor here, as are arrow functions.)  As the whole point of this RFC is to make writing common code easier, requiring redundant boilerplate for it to work is actively counter-productive.

So what I'd suggest instead is "specify the full signature if you want a custom name OR wider type; or omit the param list entirely to get a same-type $value variable, which 99% of the time is all you need."  We get that in return for documenting "$value is the default", which for someone who has already figured out $this, should be a very low effort to learn.

Also, a small nitpick: The link to your attributeutils repo in the 
examples page, is broken, and it would be nice to see a few examples 
showing the explicit version of the hooks.
Link fixed, thanks.  What do you mean explicit version of the hooks?

--Larry Garfield

Hi Larry,

Great to see you pick up on this proposal again. Rather than "just use $this->propName directly", you might want to consider yield to commit the property value, as a solution to "to assign and confirm the property write while still allowing code to run after it". But then return would be also be a logical consequence.
    public string $propName {
        set($value) { 
            yield $value;
            $this->doSomething();
            yield $value;
            return $this->doSomethingElse(); // final commit, not required
        }
    }

If I read your remarks on why not using return I do not get why this proposal wants to deviate from a normal function syntax.

function test(string $name) {
    return [$first, $last] = explode(' ', $name);
}

var_dump(test("Larry Garfield")); // returns ["Larry", "Garfield"]

That's how it always has been, no? So in your example, short code abbreviated form would not work. One has to write a block.

    public string $fullName { 
        set => [$this->first, $this->last] = explode(' ', \ucfirst($value)); // error, $fullName is a string, returning array
    }
 
    public string $fullName { 
        set {
            [$this->first, $this->last] = explode(' ', \ucfirst($value)); // no error, not returning
        }
    }

Hope this RFC will develop into something that will pass. Good luck!

Regards,
Frederik


--------------3eXaWoLJsKTlv7Y5fHbUBn0i--