Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125602 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 943BF1A00BD for ; Tue, 17 Sep 2024 20:37:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1726605569; bh=Eba3jQAr4eaxHEZtvZnKO2RuGnloYycgGGyke/sgzNY=; h=Date:From:To:In-Reply-To:References:Subject:From; b=Uwl8IrBsTf8i/zshn00iqLbzSNzIKDm56WtMcmpNeSaH1AhPppHGKupVq+fiTrqVF 5XYGj7CxCDOp12QpFS9Wdx+hCQWshhowreqQgG2DTb6KGYXn79d5a4zntfmivKEC1Q hOaAUAyp612Gopq6bvLQdZTIC50iUFACiu9KYR1rbNZESC8BvkR8CLSh+Nd6FTukvy dPaaaxsBDcHmmA7c64QJ9uqHvs4mrtvX+qbPb34u+c1BBTCRRfHpDiVqcrNwmZU1pu pt3ENTrJFD3rCJakg/xcILC7Vsl2xWb2A1ElPqlXTY/SkDVCgTr5/RQx3tXM//kBRP AUEoiEhNLmvBg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3FA70180053 for ; Tue, 17 Sep 2024 20:39: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.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout1-smtp.messagingengine.com (fout1-smtp.messagingengine.com [103.168.172.144]) (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 ; Tue, 17 Sep 2024 20:39:27 +0000 (UTC) Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfout.phl.internal (Postfix) with ESMTP id 28D3813801A1 for ; Tue, 17 Sep 2024 16:37:22 -0400 (EDT) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-01.internal (MEProxy); Tue, 17 Sep 2024 16:37:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1726605442; x=1726691842; bh=vTs5ul4M++ v+yXzoG312+7yLdDhARXemI0R5NHu+og0=; b=Uck1dVe9GGtLaU6FRxnkUaBL0X 6N/zETmdrmW1WVSmjYGaFtn0S5FOpxK+VSiCMgCv7QOHVeHnJm5Z+6+wNKS5GI50 +vVaoEE+HtetDrxcnZS+QikoY1uOc51z6i4Vo5FB52GwCtwyt3HUkffqzEHVn+Ml OoVh7PwZoKXrplhWY62vPLDweCgDXUIhdI3Farh9IR/sePE9n1TRkhuy1shmcEeP sBcbOk61bt8COMV7xPwMewmn7H7bB0Jr/8+rGjikNP9P5bM1jNgf+h5nqMEMBsIk WNfyWF8ZM/v9JnFITbl5KuKejh1YhueDA7g2t3csDgKLGpuTz1XC0Jlx6V9Q== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; t=1726605442; x=1726691842; bh=vTs5ul4M++v+yXzoG312+7yLdDhA RXemI0R5NHu+og0=; b=ch1vcQFsVqQpmnSgV2ytoAC6wWD4t2pi/6fC7tEc/Lja 4o8b7JfWU3hgyBr32itoyfhByB1H2slNJefvKTEIWu/3p53gEKbaAN2Ua0QCme82 Itr9ulnB75SKQhfNbH7Znm/MMAvSuRfOLDZhbGBs5suzzdqgmdDWq88JiPvPlquf p9XzPw+Z2Ift//zwqzIrjIfqNGt20/NfvSb8dL5icgYqBdqOLJ24xl3GNUxQvxYA 8eOsNgq2uZh4Rq7bNhET01AK/SkRCfwWDI5JazRDa70KhKNHBqmQPg5r8rYXfHXA YMEXmkCj4egIY1InROBpb257iUwVvsA5w8YQ2lJEXA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrudekjedgudehgecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdp uffrtefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecunecujfgurhepofggff fhvffkjghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgrnhguvghrshdf uceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvghrnheptdeuje dttefhueelhfdtleeiudetlefftdduleehffegtdeihefhleeijefgveegnecuvehluhhs thgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothhtlh gvugdrtghouggvshdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpdhr tghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 973D7780067; Tue, 17 Sep 2024 16:37:21 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Tue, 17 Sep 2024 22:37:01 +0200 To: internals@lists.php.net Message-ID: In-Reply-To: <0f0444eb-8fc5-4c56-8528-5aa528988e73@rwec.co.uk> References: <2551c06a-ec1f-4870-a590-aeb5752fc944@rwec.co.uk> <0f0444eb-8fc5-4c56-8528-5aa528988e73@rwec.co.uk> Subject: Re: [PHP-DEV] [Pre-RFC Discussion] User Defined Operator Overloads (again) Content-Type: multipart/alternative; boundary=a759e7aeb4b54eddb956bf25f6f7099c From: rob@bottled.codes ("Rob Landers") --a759e7aeb4b54eddb956bf25f6f7099c Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Tue, Sep 17, 2024, at 21:25, Rowan Tommins [IMSoP] wrote: > On 17/09/2024 18:15, Jordan LeDoux wrote: >>=20 >>> 1. Are we over-riding *operators* or *operations*? That is, is the u= ser=20 >>> saying "this is what happens when you put a + symbol between two Foo=20 >>> objects", or "this is what happens when you add two Foo objects toge= ther"? >>=20 >> 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. >=20 >=20 > I don't think that's really true. Take the behaviour of comparisons in= your previous RFC: if that RFC had been accepted, the user would have h= ad no way to make $a < $b and $a > $b have different behaviour, because = the same overload would be called, with the same parameters, in both cas= es. >=20 > Slightly less strict is requiring groups of operators: the Haskell "nu= m" typeclass (roughly similar to an interface) requires definitions for = all of "+", "*", "abs", "signum", "fromInteger", and either unary or bin= ary "-". It also defines the type signatures for each. If this was the o= nly way to overload the "+" operator, users would have to really go out = of their way to use it to mean something unrelated addition. >=20 > As it happens, Haskell *does* allow arbitrary operator overloads, and = in fact goes to the other extreme and allows entirely new operators to b= e invented. The same is true in PostgreSQL - you can implement the <> operator if you want to. >=20 > I think it's absolutely possible - and desirable - to choose a philoso= phical position on that spectrum, and use it to drive design decisions. = The choice of "__add" vs "operator+" is one such decision. >=20 >=20 >=20 >> =20 >> 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: >>=20 >> 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 bypasse= d entirely, so I do not need to handle this case) >> 2. If they are both objects, are they both instances of the same clas= s? 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 ov= erload of the object on the left. Does it produce a type error because i= t 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. >=20 >=20 > This is option (g) in my list, with the additional "prefer sub-classes= " rule (step 3), which I agree would be a good addition. >=20 > As noted, it doesn't provide symmetry, because step 4 depends on the o= rder in the source code. Option (c) is the same algorithm without step 4= , so guarantees that $a + $b and $b + $a will always call the same metho= d. >=20 > Options (d), (e), and (f) each add an extra step: one operand can sign= al "I don't know" and the other operand gets a chance to answer. They're= essentially ways to "partially implement" an operator. >=20 > Options (a) and (b) perform the same kind of polymorphic resolution on= *both* operands, which is how many languages work for functions and/or = methods already.=20 >=20 >=20 >=20 > Reading the C# spec, if there is more than one candidate overload whic= h is equally specific, an error is raised. I guess you could do the same= even with one implementation per class, by replacing step 4 in your alg= orithm: >=20 >=20 > > 4. If neither of them are direct descendants of the other, and only = one implements the operator, use it. > > 5. If neither of them are direct descendants of the other, and both = implement the operator, throw an error. >=20 > Let's call that option (h) :) >=20 >=20 >=20 > By the way, searching online for the phrase "Polymorphic Handler Resol= ution" finds no results other than you saying it is the name for this al= gorithm. >=20 >=20 >=20 >> This is similar to what I originally designed, and I actually moved t= o an enum based on feedback. The argument was something like `$isReverse= d` or `$left` or so on is somewhat ambiguous, while the enum makes it ex= tremely explicit. >=20 >=20 > Ah, fair enough. Explicitness vs conciseness is always a trade-off. My= thinking was that the "reversed" form would be far more rarely called t= han the "normal" form; but that depends a lot on which resolution algori= thm is used. >=20 >=20 >=20 > Regards, >=20 > --=20 > Rowan Tommins > [IMSoP] To be honest, this juggling of caller orders has me a bit concerned. For= example, matrix multiplication isn=E2=80=99t communitive, as are non-ab= elion groups in general (quaternions being another popular system), but,= I am used to Scala, where the left-hand is the one always called. I understand that this is what the operant position is for, but it strik= es me as something that extreme care has to be called for when working w= ith these types of objects when another object is involved. For example,= quaternions can be multiplied by a matrix and the order is super import= ant (used for 3d rotations) but it appears the actual method called may = not be deterministic because these classes may be unrelated, the one on = the left is called, which may or may not result in a correct answer. All= that is to say, this is just to illustrate how complex this ordering al= gorithm seems to be. Depending on how the libraries are implemented and = whether they are designed to work together.=20 I would prefer to see something simple, and easy to reason about. We can= abuse some mathematical properties to result in something quite simple: 1. If both are scalar, use existing logic.=20 2. If one is scalar and the other is not, use existing logic.=20 3. If one is scalar and the other overrides the operation, rearrange th= e operation per its communitive rules so the object is on the left. $sca= lar + $obj =3D=3D $obj + $scalar; $scalar - $obj =3D=3D -$obj + $scalar,= -($obj - $scalar). It is generally accepted (IIRC) that when scalars ar= e involved, we don=E2=80=99t need to be concerned with non-abelion group= s. 4. If both are objects, use the one on the left. I think this is much easier to reason about (you either get a scalar or = another object) that doesn=E2=80=99t involve a developer deeply understa= nding the inheritance of the objects in question or to understand the al= gorithm for choosing which one will be called.=20 =E2=80=94 Rob --a759e7aeb4b54eddb956bf25f6f7099c Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable

=
On Tue, Sep 17, 2024, at 21:25, Rowan Tommins [IMSoP] wro= te:
On 17/09/2024 18:15, Jordan LeDoux=0A wrote:

1. Are we over-riding *operators*= or *operations*? That is,=0A is the user
say= ing "this is what happens when you put a + symbol between=0A = two Foo
objects", or "this is what happens when you add = two Foo=0A objects together"?

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

=

I don't think that's really true. Take the behaviour of=0A = comparisons in your previous RFC: if that RFC had been accepted,=0A = the user would have had no way to make $a < $b and $a > $b=0A = have different behaviour, because the same overload would be=0A = called, with the same parameters, in both cases.

Slightly le= ss strict is requiring groups of operators: the=0A Haskell "num" ty= peclass (roughly similar to an interface) requires=0A definitions f= or all of "+", "*", "abs", "signum", "fromInteger",=0A and either u= nary or binary "-". It also defines the type=0A signatures for each= . If this was the only way to overload the "+"=0A operator, users w= ould have to really go out of their way to use it=0A to mean someth= ing unrelated addition.

As it happens, Haskell *does* allow ar= bitrary operator overloads,=0A and in fact goes to the other extrem= e and allows entirely new=0A operators to be invented. The same is = true in PostgreSQL - you can=0A implement the <<//-^+^-//>= >  operator if you want to.

I think it's absolutely po= ssible - and desirable - to choose a=0A philosophical position on t= hat spectrum, and use it to drive=0A design decisions. The choice o= f "__add" vs "operator+" is one such=0A decision.


 
The approach I plan to use for thi= s question has a name:=0A Polymorphic Handler Resolution. The o= verload that is executed=0A will be decided by the following se= ries of decisions:

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


This is option (g) in my list, with the additio= nal "prefer=0A sub-classes" rule (step 3), which I agree would be a= good=0A addition.

As noted, it doesn't provide symmetry,= because step 4 depends on=0A the order in the source code. Option = (c) is the same algorithm=0A without step 4, so guarantees that $a = + $b and $b + $a will always=0A call the same method.

Opt= ions (d), (e), and (f) each add an extra step: one operand can=0A s= ignal "I don't know" and the other operand gets a chance to=0A answ= er. They're essentially ways to "partially implement" an=0A operato= r.

Options (a) and (b) perform the same kind of polymorphic=0A= resolution on *both* operands, which is how many languages work=0A= for functions and/or methods already. 


R= eading the C# spec, if there is more than one candidate overload=0A = which is equally specific, an error is raised. I guess you could=0A = do the same even with one implementation per class, by replacing=0A = step 4 in your algorithm:

> 4. If neither of th= em are direct descendants of the other,=0A and only one implements = the operator, use it.
> 5. If neither of them are dire= ct descendants of the other,=0A and both implement the operator, th= row an error.

Let's call that option (h) :)


By the way, searching online for the phrase "Polymorphic Han= dler=0A Resolution" finds no results other than you saying it is th= e name=0A for this algorithm.


Thi= s is similar to what I originally designed, and I=0A actually= moved to an enum based on feedback. The argument=0A was some= thing like `$isReversed` or `$left` or so on is=0A somewhat a= mbiguous, while the enum makes it extremely=0A explicit.
<= /div>


Ah, fair enough. Explicitnes= s vs conciseness is always a=0A trade-off. My thinking was that the= "reversed" form would be far=0A more rarely called than the "norma= l" form; but that depends a lot=0A on which resolution algorithm is= used.


Regards,

--=20=0ARowan Tommins=0A[IMSoP]

To be honest, this juggling of caller orders has me a = bit concerned. For example, matrix multiplication isn=E2=80=99t communit= ive, as are non-abelion groups in general (quaternions being another pop= ular system), but, I am used to Scala, where the left-hand is the one al= ways called.

I understand that this is what= the operant position is for, but it strikes me as something that extrem= e care has to be called for when working with these types of objects whe= n another object is involved. For example, quaternions can be multiplied= by a matrix and the order is super important (used for 3d rotations) bu= t it appears the actual method called may not be deterministic because t= hese classes may be unrelated, the one on the left is called, which may = or may not result in a correct answer. All that is to say, this is just = to illustrate how complex this ordering algorithm seems to be. Depending= on how the libraries are implemented and whether they are designed to w= ork together. 

I would prefer to see s= omething simple, and easy to reason about. We can abuse some mathematica= l properties to result in something quite simple:

  1. If both are scalar, use existing logic. 
  2. If o= ne is scalar and the other is not, use existing logic. 
  3. If one is scalar and the other overrides the operation, rearrange the o= peration per its communitive rules so the object is on the left. $scalar= + $obj =3D=3D $obj + $scalar; $scalar - $obj =3D=3D -$obj + $scalar, -(= $obj - $scalar). It is generally accepted (IIRC) that when scalars are i= nvolved, we don=E2=80=99t need to be concerned with non-abelion groups.<= br>
  4. If both are objects, use the one on the left.
<= div>I think this is much easier to reason about (you either get a scalar= or another object) that doesn=E2=80=99t involve a developer deeply unde= rstanding the inheritance of the objects in question or to understand th= e algorithm for choosing which one will be called. 

<= /div>
=E2=80=94 Rob
--a759e7aeb4b54eddb956bf25f6f7099c--