Newsgroups: php.internals
Path: news.php.net
Xref: news.php.net php.internals:125589
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 8C5A11A00BD
	for <internals@lists.php.net>; Tue, 17 Sep 2024 17:15:55 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail;
	t=1726593480; bh=uhxSS+GmRreSbQJLYV6O5MtBtSXQRChYvJ/WAC43l4A=;
	h=References:In-Reply-To:From:Date:Subject:To:Cc:From;
	b=iiQP1QTSbv3c0JNaoL9iCNe7vsGWPr/7i9kcB2JBhbYZLUWPwFE5FmVAeRmz8EANL
	 tCOfziyUAI/joRtp9Vq/xtc17IqRB6hjGN6ZnxZ7Wgc+hBxTNjMTTt9cTS0GKdYizU
	 /dkzC68N/pvksver+RoRz1HRd/F2OmQiXzwbs6H5hahr3Gs027VsbnlMqPe/5qJGQo
	 xHTKgqh2/Iu3rt2weea+azPNWTlAZJCGlaqkcbO0OX4ZIUkBHB3WEuXUT/x+0fLFG4
	 i7hqp7U4PUlrAGOCEe7aDrB/GQxXWIxLXBcHKxG9HzMUcq+GrniPLAlnJZAPDFZ4fW
	 +vRyqi131ZW8Q==
Received: from php-smtp4.php.net (localhost [127.0.0.1])
	by php-smtp4.php.net (Postfix) with ESMTP id 886D6180084
	for <internals@lists.php.net>; Tue, 17 Sep 2024 17:17:59 +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,FREEMAIL_FROM,
	HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,
	SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0
X-Spam-Virus: No
X-Envelope-From: <jordan.ledoux@gmail.com>
Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178])
	(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 <internals@lists.php.net>; Tue, 17 Sep 2024 17:17:59 +0000 (UTC)
Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2068acc8b98so53341025ad.3
        for <internals@lists.php.net>; Tue, 17 Sep 2024 10:15:53 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20230601; t=1726593353; x=1727198153; darn=lists.php.net;
        h=cc:to:subject:message-id:date:from:in-reply-to:references
         :mime-version:from:to:cc:subject:date:message-id:reply-to;
        bh=uhxSS+GmRreSbQJLYV6O5MtBtSXQRChYvJ/WAC43l4A=;
        b=UkrcL67Lx/dPkf9y3wjlx6zPU3RLZuJhBP7cUQ2taKryAKa2pt2J5Ud6bb3gTk1BRO
         McK09P+a6Qhh1tUhR7Ihwq2Y0tQ8Ax1Vf1LmrI2Nm00qMn41sLx/PQr4ZDz+v28t+sI+
         wQk+v3EjeFG7V6pcbsCZNTD8pB5L+8KREo2F/oc9Y3gYD9LEw9+vGq1z2tHFPnV9d16i
         jIWjpWk0Ed9ixvIqzmuWwtJBnUYzszUIeHC+Urq8VEpxpASguDYt9EDbBBJrwEdfZWoU
         3gNQG0zeyIUEwe9EUn+wUsWFeXy1vv4xhan9Xx68Zv09qlnS7ep9rT3v24/Vse9aL95a
         EbFg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20230601; t=1726593353; x=1727198153;
        h=cc: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=uhxSS+GmRreSbQJLYV6O5MtBtSXQRChYvJ/WAC43l4A=;
        b=NnC3NpZFXY5/LtZg+SWnZHrh66ZdZTlsA9cHnJS2XJqF3ffkv1160LxYku8w+3eu9S
         ILCNRlOUZDDonI3eIhV30KZV9TLEt64RdCEWWa80qtL2occQ2WyH4oHH+b42JdoaGbsv
         fKtvSh/d4esxxiWxRP2Mdp9Q8YLOXaJ+Nw0dsr3LngkOOqmqTkKRIA9L3KFSnmILgXAY
         s8NgveVlRCz74PF8/Ua9Ii6Sbmv2msW2A2DirNZTfD86NAW/cebUp7YU2+xGwAbPnguH
         tV4XfvLPHlWwoYGZCljopV+G8I9XwraUPoIww/Q/oHw8UOL4dOxgOo+RykLcpOn3WANV
         L5HA==
X-Gm-Message-State: AOJu0YzRKVSxvvDuIO5YcMPRgBlD1MkEMcWLth6Ikpc+SBiKO9LFdfQG
	vrMUGGJ2S7aPmP0yjLWdF6hAFqUHFmtpttpaSlicvqc42XBE6A6PN5Br8vc7rVjj/XxDgp2BygD
	hINO8oOui4RH+UcjDbjxu/5/BIfGbiw==
X-Google-Smtp-Source: AGHT+IFODAHTL+yLaKslYtkJbIpHlLYK4SWiEt/TqI8XmxVyGuwUqfoj3V/sEkGsEfWt43OQ/jd7XXqTa9YMDM+TSm0=
X-Received: by 2002:a17:902:d54a:b0:202:9b7:1dc with SMTP id
 d9443c01a7336-2076e44ba5emr268214575ad.54.1726593352741; Tue, 17 Sep 2024
 10:15:52 -0700 (PDT)
Precedence: bulk
list-help: <mailto:internals+help@lists.php.net
list-unsubscribe: <mailto:internals+unsubscribe@lists.php.net>
list-post: <mailto:internals@lists.php.net>
List-Id: internals.lists.php.net
x-ms-reactions: disallow
MIME-Version: 1.0
References: <CAMrTa2E2m00gOoGy5FbvkrLwM1AUP3ZmJXHkxh_R2qEszQ=NWA@mail.gmail.com>
 <2551c06a-ec1f-4870-a590-aeb5752fc944@rwec.co.uk>
In-Reply-To: <2551c06a-ec1f-4870-a590-aeb5752fc944@rwec.co.uk>
Date: Tue, 17 Sep 2024 10:15:40 -0700
Message-ID: <CAMrTa2FLJwjwy9uGpRJJJFMUD1vs=vqzv8x2VxMsYeBqMN4m9Q@mail.gmail.com>
Subject: Re: [PHP-DEV] [Pre-RFC Discussion] User Defined Operator Overloads (again)
To: "Rowan Tommins [IMSoP]" <imsop.php@rwec.co.uk>
Cc: internals@lists.php.net
Content-Type: multipart/alternative; boundary="000000000000763df3062253d725"
From: jordan.ledoux@gmail.com (Jordan LeDoux)

--000000000000763df3062253d725
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

On Tue, Sep 17, 2024 at 1:18=E2=80=AFAM Rowan Tommins [IMSoP] <imsop.php@rw=
ec.co.uk>
wrote:

> On 14/09/2024 22:48, Jordan LeDoux wrote:
> >
> > 1. Should the next version of this RFC use the `operator` keyword, or
> > should that approach be abandoned for something more familiar? Why do
> > you feel that way?
> >
> > 2. Should the capability to overload comparison operators be provided
> > in the same RFC, or would it be better to separate that into its own
> > RFC? Why do you feel that way?
> >
> > 3. Do you feel there were any glaring design weaknesses in the
> > previous RFC that should be addressed before it is re-proposed?
> >
>
> I think there are two fundamental decisions which inform a lot of the
> rest of the design:
>
> 1. Are we over-riding *operators* or *operations*? That is, is the user
> saying "this is what happens when you put a + symbol between two Foo
> objects", or "this is what happens when you add two Foo objects together"=
?
>

If we allow developers to define arbitrary code which is executed as a
result of an operator, we will always end up allowing the first one.


> 2. How do we despatch a binary operator to one of its operands? That is,
> given $a + $b, where $a and $b are objects of different classes, how do
> we choose which implementation to run?
>
>
This is something not many other people have been interested in so far, but
interestingly there is a lot of prior art on this question in other
languages! :)

The best approach, from what I have seen and developer usage in other
languages, is somewhat complicated to follow, but I will do my best to make
sure it is understandable to anyone who happens to be following this thread
on internals.

The approach I plan to use for this question has a name: Polymorphic
Handler Resolution. The overload that is executed will be decided by the
following series of decisions:

1. Are both of the operands objects? If not, use the overload on the one
that is. (NOTE: if neither are objects, the new code will be bypassed
entirely, so I do not need to handle this case)
2. If they are both objects, are they both instances of the same class? If
they are, use the overload of the one on the left.
3. If they are not objects of the same class, is one of them a direct
descendant of the other? If so, use the overload of the descendant.
4. If neither of them are direct descendants of the other, use the overload
of the object on the left. Does it produce a type error because it does not
accept objects of the type in the other position? Return the error and
abort instead of re-trying by using the overload on the right.

This results from what it means to `extend` a class. Suppose you have a
class `Foo` and a class `Bar` that extends `Foo`. If both `Foo` and `Bar`
implement an overload, that means `Bar` inherited an overload. It is either
the same as the overload from `Foo`, in which case it shouldn't matter
which is executed, or it has been updated with even more specific logic
which is aware of the extra context that `Bar` provides, in which case we
want to execute the updated implementation.

So the implementation on the left would almost always be executed, unless
the implementation on the right comes from a class that is a direct
descendant of the class on the left.

`Foo + Bar`
`Bar + Foo`

In practice, you would very rarely (if ever) use two classes from entirely
different class inheritance hierarchies in the same overload. That would
closely tie the two classes together in a way that most developers try to
avoid, because the implementation would need to be aware of how to handle
the classes it accepts as an argument.

The exception to this that I can imagine is something like a container,
that maybe does not care what class the other object is because it doesn't
mutate it, only store it.

But for virtually every real-world use case, executing the overload for the
child class regardless of its position would be preferred, because
overloads will tend to be confined to the core types of PHP + the classes
that are part of the hierarchy the overload is designed to interact with.


>
>
> Finally, a very quick note on the OperandPosition enum: I think just a
> "bool $isReversed" would be fine - the "natural" expansion of "$a+$b" is
> "$a->operator+($b, false)"; the "fallback" is "$b->operator+($a, true)"
>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>

This is similar to what I originally designed, and I actually moved to an
enum based on feedback. The argument was something like `$isReversed` or
`$left` or so on is somewhat ambiguous, while the enum makes it extremely
explicit.

However, it's not a design detail I am committed to. I just want to let you
know why it was done that way.

Jordan

--000000000000763df3062253d725
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr"><br></div><br><div class=3D"gmail_quote">=
<div dir=3D"ltr" class=3D"gmail_attr">On Tue, Sep 17, 2024 at 1:18=E2=80=AF=
AM Rowan Tommins [IMSoP] &lt;<a href=3D"mailto:imsop.php@rwec.co.uk">imsop.=
php@rwec.co.uk</a>&gt; wrote:<br></div><blockquote class=3D"gmail_quote" st=
yle=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padd=
ing-left:1ex">On 14/09/2024 22:48, Jordan LeDoux wrote:<br>
&gt;<br>
&gt; 1. Should the next version of this RFC use the `operator` keyword, or =
<br>
&gt; should that approach be abandoned for something more familiar? Why do =
<br>
&gt; you feel that way?<br>
&gt;<br>
&gt; 2. Should the capability to overload comparison operators be provided =
<br>
&gt; in the same RFC, or would it be better to separate that into its own <=
br>
&gt; RFC? Why do you feel that way?<br>
&gt;<br>
&gt; 3. Do you feel there were any glaring design weaknesses in the <br>
&gt; previous RFC that should be addressed before it is re-proposed?<br>
&gt;<br>
<br>
I think there are two fundamental decisions which inform a lot of the <br>
rest of the design:<br>
<br>
1. Are we over-riding *operators* or *operations*? That is, is the user <br=
>
saying &quot;this is what happens when you put a + symbol between two Foo <=
br>
objects&quot;, or &quot;this is what happens when you add two Foo objects t=
ogether&quot;?<br></blockquote><div><br></div><div>If we allow developers t=
o define arbitrary code which is executed as a result of an operator, we wi=
ll always end up allowing the first one.<br></div><div>=C2=A0</div><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px=
 solid rgb(204,204,204);padding-left:1ex">
2. How do we despatch a binary operator to one of its operands? That is, <b=
r>
given $a + $b, where $a and $b are objects of different classes, how do <br=
>
we choose which implementation to run?<br>
<br></blockquote><div><br></div><div>This is something not many other peopl=
e have been interested in so far, but interestingly there is a lot of prior=
 art on this question in other languages! :) <br></div><div><br></div><div>=
The best approach, from what I have seen and developer usage in other langu=
ages, is somewhat complicated to follow, but I will do my best to make sure=
 it is understandable to anyone who happens to be following this thread on =
internals.</div><div><br></div><div>The approach I plan to use for this que=
stion has a name: Polymorphic Handler Resolution. The overload that is exec=
uted will be decided by the following series of decisions:</div><div><br></=
div><div>1. Are both of the operands objects? If not, use the overload on t=
he one that is. (NOTE: if neither are objects, the new code will be bypasse=
d entirely, so I do not need to handle this case)</div><div>2. If they are =
both objects, are they both instances of the same class? If they are, use t=
he overload of the one on the left.</div><div>3. If they are not objects of=
 the same class, is one of them a direct descendant of the other? If so, us=
e the overload of the descendant.</div><div>4. If neither of them are direc=
t descendants of the other, use the overload of the object on the left. Doe=
s it produce a type error because it does not accept objects of the type in=
 the other position? Return the error and abort instead of re-trying by usi=
ng the overload on the right.</div><div><br></div><div>This results from wh=
at it means to `extend` a class. Suppose you have a class `Foo` and a class=
 `Bar` that extends `Foo`. If both `Foo` and `Bar` implement an overload, t=
hat means `Bar` inherited an overload. It is either the same as the overloa=
d from `Foo`, in which case it shouldn&#39;t matter which is executed, or i=
t has been updated with even more specific logic which is aware of the extr=
a context that `Bar` provides, in which case we want to execute the updated=
 implementation.</div><div><br></div><div>So the implementation on the left=
 would almost always be executed, unless the implementation on the right co=
mes from a class that is a direct descendant of the class on the left.</div=
><div><br></div><div>`Foo + Bar`</div><div>`Bar + Foo`<br></div><div><br></=
div><div>In practice, you would very rarely (if ever) use two classes from =
entirely different class inheritance hierarchies in the same overload. That=
 would closely tie the two classes together in a way that most developers t=
ry to avoid, because the implementation would need to be aware of how to ha=
ndle the classes it accepts as an argument.</div><div><br></div><div>The ex=
ception to this that I can imagine is something like a container, that mayb=
e does not care what class the other object is because it doesn&#39;t mutat=
e it, only store it.</div><div><br></div><div>But for virtually every real-=
world use case, executing the overload for the child class regardless of it=
s position would be preferred, because overloads will tend to be confined t=
o the core types of PHP + the classes that are part of the hierarchy the ov=
erload is designed to interact with.<br></div><div>=C2=A0</div><blockquote =
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px sol=
id rgb(204,204,204);padding-left:1ex">
<br>
<br>
Finally, a very quick note on the OperandPosition enum: I think just a <br>
&quot;bool $isReversed&quot; would be fine - the &quot;natural&quot; expans=
ion of &quot;$a+$b&quot; is <br>
&quot;$a-&gt;operator+($b, false)&quot;; the &quot;fallback&quot; is &quot;=
$b-&gt;operator+($a, true)&quot;<br>
<br>
<br>
Regards,<br>
<br>
-- <br>
Rowan Tommins<br>
[IMSoP]<br></blockquote><div><br></div><div>This is similar to what I origi=
nally designed, and I actually moved to an enum based on feedback. The argu=
ment was something like `$isReversed` or `$left` or so on is somewhat ambig=
uous, while the enum makes it extremely explicit.</div><div><br></div><div>=
However, it&#39;s not a design detail I am committed to. I just want to let=
 you know why it was done that way.</div><div><br></div><div>Jordan<br></di=
v></div></div>

--000000000000763df3062253d725--