Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122510 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 47E911AD8F6 for ; Mon, 26 Feb 2024 23:11:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1708989089; bh=bvNfMKk5fgvVreVwwPtAQjkzsMHyCP3GtubtaaB3h4o=; h=Date:Subject:To:References:From:In-Reply-To:From; b=RGXuDUhYfjwJrG/a3hFWI3EJgPiFPsIns9K1WaogSVQGESo/38ruihM++LHy5t42E CdglW//fv4S57409LG0IWBfEX9QDrA81Voyt7XPtx9YNM2GDCJWtXDBNPsDv/xdRqQ XZHsnfNg1un7BwtL9Jsfxd3N7WIJlOWHqGiSlvFn9PVP55babAJZeKtwqiZUEYGwUx 2vzrewDft39zpIklh5/FS0a60zQvfT4uzxhFJCYUgjKHnaxQdzQ+nxgvtiWbwuRCFM RtHVz99XtahkPK+Rt3XiXQMN5wXWbL2P3EfglzV2+y8MsAh7TUhRYPfce+0RlGxVVG 232YYNnUbsMig== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 0F3351805CA for ; Mon, 26 Feb 2024 23:11:28 +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=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,HTML_MESSAGE, SPF_HELO_NONE,SPF_PASS,T_KAM_HTML_FONT_INVALID,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 15:11:27 -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:From:References:To:Subject: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=CMRP+3oi29146JNv6K9vVxSbq03EiXSgEN/kdHfC+G0=; b=UACLGEuUFSb3c4syeAxuYDhCJL VscRAzGZS65Pb9+T47wuj9aKMa+skaz0PtOpgXW5KJiJeJOXfWhBjuUp1I62IyqQYtS3P4qQ+h4XI g1Wi0s/b6jFsSSHc8q9O4lzi3dZrlmEwS9G3FKcDT+8MoYbNZzU+ISZNNtQuPnBZAQTrDgTxWxH/l Grwgo/ceVKDcvwsinOlOvRaI5G+REmAEyuX5iLeVX9/vrDHgfR2qfXteml1ClmUlOOp2fhAivCCq5 /k94O7iDuuTsyiPnfT0fTD2JXQ369r9273z4Ak8GJsLmThMTYJP7UiY5gMaLxe7QATfvqNa5D4nSx xw+1Cytg==; 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 1rek7x-000Fvf-0h for internals@lists.php.net; Mon, 26 Feb 2024 23:11:17 +0000 Content-Type: multipart/alternative; boundary="------------4zJVBH7WnNLP07CZoMs0eq6r" Message-ID: <963f5cc5-cdb1-4384-b519-5cb15640654e@genkgo.nl> Date: Tue, 27 Feb 2024 00:11:16 +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 Content-Language: en-US, nl-NL 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> <2fdf1933-b51c-40cc-8d02-31899b96c71c@genkgo.nl> <95e93cb9-3ab0-4cf3-8ec5-83e74c9dd607@genkgo.nl> <876aff9f-3eae-4d2d-8e3f-30dfbbeed49c@rwec.co.uk> In-Reply-To: <876aff9f-3eae-4d2d-8e3f-30dfbbeed49c@rwec.co.uk> From: f.bosch@genkgo.nl (Frederik Bosch) This is a multi-part message in MIME format. --------------4zJVBH7WnNLP07CZoMs0eq6r Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hi Rowan, On 26-02-2024 23:46, Rowan Tommins [IMSoP] wrote: > On 26/02/2024 20:21, Frederik Bosch wrote: >> I do note that $this->propName might suggest that the backing value >> is accessible from other locations than only the property's own >> get/set methods, because of $this usage. > > > Yes, I actually stumbled over that confusion when I was writing some > of the examples in my lengthy e-mail in this thread. As I understand > it, this would work: > > public string $foo { >     get { $this->foo ??= 0; $this->foo++; return $this->foo; } >     set { throw new Exception; } > } > > Outside the hooks, trying to write to $this->foo would throw the > exception, because it refers to the hooked property as a whole; but > inside, the same name refers to something different, which isn't > accessible anywhere else. > > Now that I've looked more at how Kotlin uses "field", I understand why > it makes sense - it's not an alias for the property itself, but the > way to access a "backing store" which has no other name. > > Using $this->foo as the name is tempting if you think of hooks as > happening "on top of" the "real" property; but that would be a > different feature, like Switft's "property observers" (willSet and > didSet). What's really happening is that we're declaring two things at > once, and giving them the same name; almost as if we'd written this: > > public string $foo { >     get { static $_foo; $_foo ??= 0; $_foo++; return $_foo; } >     set { throw new Exception; } > } > > Kotlin's "field" is kind of the equivalent of that "static $_foo" > > And what happens in the following situation, how are multiple get calls working together? public string $fullName {     get => $this->first . ' ' . $this->last; // is this accessing the backed value, or is it accessing via get     set($value) => $this->fullName = $value; } public string $first {     get => explode(' ', $this->fullName)[0], // is this accessing the backed value, or is it accessing via get     set($value) => $value; } Isn't it weird that $this->propName gives different results from one get function, compared to the other. I would say $this->prop should always follow the same semantics as explained in the RFC (first __get/__set, then the accessor). > >> Regarding returning void=null, this is something that IDE and static >> analyzers already pick-up as an error. I think being stricter on that >> in this RFC would actually make sense, and treat void not as null. >> > > What would happen if a setter contained both "return 42;" and > "return;"? The latter is explicitly allowed in "void" functions, but > is also allowed in a non-void function as meaning "return null;" return 42; // returns (int)42 return; // early return, void, same as no return return null; // returns null > > >> And why yield is magic, I do not get that. The word and the >> expression actually expresses that something is, well, yielded. >> > > But yielded to where? My mental model of "return to set" is that this: > > public string $name { set($value) { $x = something($value); return $x > + 1; } } > > Is effectively: > > private function _name_set($value) { $x = something($value); return $x > + 1; } } > plus: > $this->name = $this->_name_set($value); > > With "yield", I can't picture that simple translation; the "magic" is > whatever translates the "yield" keyword into "$this->name =" You would picture it by explaining how it works from the source side. A set function that contains a yield turns the set function into a directly consumed generator. Considering the following: public string $first {     set($value) => {         yield 'First name';         yield 'Given name';         return 'My name';     } } the pseudo-code from the PHP source side would look as follows. $generator = setCall($class, 'first', $value); foreach ($generator as $value) {    writeProperty($class, 'first', $value); } if ($generator->hasReturn()) { writeProperty($class, 'first', $generator->getReturn()); } > > I would file it with the type widening in the RFC: seems kind of cool, > but probably isn't worth the added complexity. > > > Regards, > -- Frederik Bosch Partner Genkgo logo Mail: f.bosch@genkgo.nl Web: support.genkgo.com Entrada 123 Amsterdam +31 20 244 1920 Genkgo B.V. staat geregistreerd bij de Kamer van Koophandel onder nummer 56501153 --------------4zJVBH7WnNLP07CZoMs0eq6r Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit

Hi Rowan,

On 26-02-2024 23:46, Rowan Tommins [IMSoP] wrote:
On 26/02/2024 20:21, Frederik Bosch wrote:
I do note that $this->propName might suggest that the backing value is accessible from other locations than only the property's own get/set methods, because of $this usage.


Yes, I actually stumbled over that confusion when I was writing some of the examples in my lengthy e-mail in this thread. As I understand it, this would work:

public string $foo {
    get { $this->foo ??= 0; $this->foo++; return $this->foo; }
    set { throw new Exception; }
}

Outside the hooks, trying to write to $this->foo would throw the exception, because it refers to the hooked property as a whole; but inside, the same name refers to something different, which isn't accessible anywhere else.

Now that I've looked more at how Kotlin uses "field", I understand why it makes sense - it's not an alias for the property itself, but the way to access a "backing store" which has no other name.

Using $this->foo as the name is tempting if you think of hooks as happening "on top of" the "real" property; but that would be a different feature, like Switft's "property observers" (willSet and didSet). What's really happening is that we're declaring two things at once, and giving them the same name; almost as if we'd written this:

public string $foo {
    get { static $_foo; $_foo ??= 0; $_foo++; return $_foo; }
    set { throw new Exception; }
}

Kotlin's "field" is kind of the equivalent of that "static $_foo"


And what happens in the following situation, how are multiple get calls working together?

public string $fullName {
    get => $this->first . ' ' . $this->last; // is this accessing the backed value, or is it accessing via get
    set($value) => $this->fullName = $value;
}

public string $first {
    get => explode(' ', $this->fullName)[0], // is this accessing the backed value, or is it accessing via get
    set($value) => $value;
}

Isn't it weird that $this->propName gives different results from one get function, compared to the other. I would say $this->prop should always follow the same semantics as explained in the RFC (first __get/__set, then the accessor).


Regarding returning void=null, this is something that IDE and static analyzers already pick-up as an error. I think being stricter on that in this RFC would actually make sense, and treat void not as null.


What would happen if a setter contained both "return 42;" and "return;"? The latter is explicitly allowed in "void" functions, but is also allowed in a non-void function as meaning "return null;"
return 42; // returns (int)42
return; // early return, void, same as no return
return null; // returns null


And why yield is magic, I do not get that. The word and the expression actually expresses that something is, well, yielded.


But yielded to where? My mental model of "return to set" is that this:

public string $name { set($value) { $x = something($value); return $x + 1; } }

Is effectively:

private function _name_set($value) { $x = something($value); return $x + 1; } }
plus:
$this->name = $this->_name_set($value);

With "yield", I can't picture that simple translation; the "magic" is whatever translates the "yield" keyword into "$this->name ="

You would picture it by explaining how it works from the source side. A set function that contains a yield turns the set function into a directly consumed generator. Considering the following:

public string $first {
    set($value) => {
        yield 'First name';
        yield 'Given name';
        return 'My name';
    }
}

the pseudo-code from the PHP source side would look as follows.

$generator = setCall($class, 'first', $value);
foreach ($generator as $value) {
   writeProperty($class, 'first', $value);
}
if ($generator->hasReturn()) {
 
writeProperty($class, 'first', $generator->getReturn());
}



I would file it with the type widening in the RFC: seems kind of cool, but probably isn't worth the added complexity.


Regards,

--

Frederik Bosch

Partner


Mail:
Web: support.genkgo.com

Entrada 123
Amsterdam
+31 20 244 1920

Genkgo B.V. staat geregistreerd bij de Kamer van Koophandel onder nummer 56501153
--------------4zJVBH7WnNLP07CZoMs0eq6r--