[VOTE] Userspace operator overloading

  109241
March 23, 2020 17:58 jan.h.boehmer@gmx.de
Hi internals,

I have opened voting on
https://wiki.php.net/rfc/userspace_operator_overloading, which allows users
to overload operators in their own classes.

Voting closes on 2020-04-06.

Regards,
Jan Böhmer
  109263
March 24, 2020 10:03 ocramius@gmail.com (Marco Pivetta)
Hey Jan,

Just posting here why I voted "no": it is not your implementation proposal,
but rather the concept per-se that IMO shouldn't land in the language.

Operator overloading makes call-site code reading extremely hard, and it
makes the language much more complex for very little benefit.

Everything suggested in the RFC can be done by using explicit arrows: `->`
(method calls), which lead to expressively named methods and parameters.

I have posted similar thoughts about `->__toString()` and `->toString()`
when it comes to cast operations vs explicit calls at
https://github.com/ShittySoft/symfony-live-berlin-2018-doctrine-tutorial/pull/3#issuecomment-460441229

Overall, without type classes and infix functions, operator overloading is,
IMO, just messy.

Greets,

Marco Pivetta

http://twitter.com/Ocramius

http://ocramius.github.com/


On Mon, Mar 23, 2020 at 6:58 PM boehmer@gmx.de> wrote:

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users > to overload operators in their own classes. > > Voting closes on 2020-04-06. > > Regards, > Jan Böhmer > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  109264
March 24, 2020 10:06 sebastian@php.net (Sebastian Bergmann)
Am 24.03.2020 um 11:03 schrieb Marco Pivetta:
> Just posting here why I voted "no": it is not your implementation proposal, > but rather the concept per-se that IMO shouldn't land in the language.
I voted "no" for the same reason.
  109321
March 26, 2020 06:09 sebastian@php.net (Sebastian Bergmann)
Am 24.03.2020 um 11:06 schrieb Sebastian Bergmann:
> I voted "no" for the same reason.
I changed my vote to "yes" because of Nikita's arguments.
  109355
March 26, 2020 19:55 matthewmatthew@gmail.com (Matthew Brown)
I accept that it adds an extra level of understanding to the language – if
you see

function addAmounts($a, $b) { return $a + $b; }

you no longer definitely know the answer will be numeric.

However, I imagine that the sorts of people who will use this are _also_
the sorts of people who would add type annotations clearly – so you'd much
more likely see

function addAmounts(CurrencyAmount $a, CurrencyAmount $b) { return $a + $b;
}

where it's trivial to understand that there's userspace operator
overloading going on.

On Tue, 24 Mar 2020 at 06:04, Marco Pivetta <ocramius@gmail.com> wrote:

> Hey Jan, > > Just posting here why I voted "no": it is not your implementation proposal, > but rather the concept per-se that IMO shouldn't land in the language. > > Operator overloading makes call-site code reading extremely hard, and it > makes the language much more complex for very little benefit. > > Everything suggested in the RFC can be done by using explicit arrows: `->` > (method calls), which lead to expressively named methods and parameters. > > I have posted similar thoughts about `->__toString()` and `->toString()` > when it comes to cast operations vs explicit calls at > > https://github.com/ShittySoft/symfony-live-berlin-2018-doctrine-tutorial/pull/3#issuecomment-460441229 > > Overall, without type classes and infix functions, operator overloading is, > IMO, just messy. > > Greets, > > Marco Pivetta > > http://twitter.com/Ocramius > > http://ocramius.github.com/ > > > On Mon, Mar 23, 2020 at 6:58 PM boehmer@gmx.de> wrote: > > > Hi internals, > > > > I have opened voting on > > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > > users > > to overload operators in their own classes. > > > > Voting closes on 2020-04-06. > > > > Regards, > > Jan Böhmer > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: http://www.php.net/unsub.php > > > > >
  109266
March 24, 2020 10:41 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Mar 23, 2020 at 6:58 PM boehmer@gmx.de> wrote:

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users > to overload operators in their own classes. > > Voting closes on 2020-04-06. > > Regards, > Jan Böhmer >
To offer a counter-point, I voted yes on this proposal. A couple of thoughts: 1. This is exposing functionality that already exists for internal classes to userland classes. As a general rule, I think we should always strive to have behavioral parity between internal and userland classes. 2. Because this just exposes existing functionality, the amount of technical complexity this introduces is very small, especially compared to the significance of what this enables. The implementation complexity of RFCs is increasingly becoming a problem, due to the complex way in which existing language features interact. This RFC does not fall into that category. 3. As mentioned, this functionality already exists internally and is used by GMP, where it works (imho) very well. Of course, this is also something of a poster-child use case for operator overloading, in that the object literally represents a number. But there are plenty other similar examples, some of them very relevant to PHP (money represention). I think there is a somewhat irrational fear that operator overloading is going to be misused... but having worked in quite a few languages that do support operator overloading, I think I've only encountered this in two instances: First, the infamous use of << and >> as stream operators in C++ (which is really horrible, and gives operator overloading a bad name everywhere). And second, the use of / as a path concatenation operator in some libraries. And I think that's it. All other uses I've worked with were things like arbitrary-precision integers, floats, decimals or rationals; complex numbers, vectors, matrixes or tensors; bit vectors, constant ranges, known bits representations. Operator overloading is not a feature that is commonly needed or should be used much, but it does tend to make code a *lot* more readable in the cases where it is useful. I think people might also find this blog post by Guido interesting: https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html Regards, Nikita
  109287
March 25, 2020 11:23 nicolas.grekas+php@gmail.com (Nicolas Grekas)
> 1. This is exposing functionality that already exists for internal classes >
2. Because this just exposes existing functionality, the amount of
> technical complexity this introduces is very small >
> 3. As mentioned, this functionality already exists internally and is used > by GMP, where it works (imho) very well. >
Thanks Nikita for the insights, that's really helpful. I'd suggest removing the "PHP_OPERAND_TYPES_NOT_SUPPORTED" constant and settle on null instead. The reason is visible in the RFC: `public static function __mul($lhs, $rhs): ?Vector3` - nothing else than null can be returned here when looking at the return type. Returning a const is just calling for "WTF" indirections IMHO. I'm still voting yes, hoping for this const to be removed after if the RFC passes, if it does :) Nicolas
  109268
March 24, 2020 11:14 marandall@php.net (Mark Randall)
On 23/03/2020 17:58, jan.h.boehmer@gmx.de wrote:
> I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows users > to overload operators in their own classes.
I am certainly not opposed to operator overloading, and would like to see it in the language. However, I'll be voting no because of a couple of implementation points: 1. I feel like the choice of NULL as the return type for not implemented is far from ideal, especially for 8.0 which provides for union type hints. 2. The error handling behaviour is too timid. If operator overloading is to become a first-class feature, error handling should reflect as such and any attempt to use objects without the appropriate handlers being installed really should result in an error being thrown just as it would be if an undefined method was called. Mark Randall
  109284
March 25, 2020 10:44 kontakt@beberlei.de (Benjamin Eberlei)
On Mon, Mar 23, 2020 at 6:58 PM boehmer@gmx.de> wrote:

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users > to overload operators in their own classes. > > Voting closes on 2020-04-06. > > Regards, > Jan Böhmer >
Thank you. I voted yes on this for similar reasons than Nikita. I think it is confusing to users why internal objects can overload these and userland can't. The simplicity of exposing this is another plus. The potential for misuse is a non issue for me, because essentially this applies to most features, especially with existing magic methods we have a precedent that we can build upon instead of "repressing use".
> > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  109288
March 25, 2020 11:28 cmbecker69@gmx.de ("Christoph M. Becker")
On 23.03.2020 at 18:58, jan.h.boehmer@gmx.de wrote:

> I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows users > to overload operators in their own classes.
It seems to me that the RFC is not sufficiently specific enough regarding the concatenation of instances of classes which implement __toString(). The RFC itself doesn't mention that explicitly, and the backward incompatible changes section states: | As long as the user does not implement, the operator magic functions, | operators on objects will behave in the previous way. However, at least the current implementation raises two notices ("You have to implement the __concat function") when concatenating Stringable objects. A minor issue: in my opinion, introducing PHP_OPERAND_TYPES_NOT_SUPPORTED doesn't make sense. The RFC says: | Handlers can specify return typehints, but note that the return type | has to be nullable (as PHP_OPERAND_TYPES_NOT_SUPPORTED has the value | null). So if we ever wanted to change the value of PHP_OPERAND_TYPES_NOT_SUPPORTED, we'd introduce a BC break. Therefore the constant's value likely will never change, so using NULL directly to signal unsupported operand types would be fine, wouldn't it? -- Christoph M. Becker
  109320
March 26, 2020 02:49 jakob@givoni.dk (Jakob Givoni)
On Wed, Mar 25, 2020 at 6:28 AM Christoph M. Becker <cmbecker69@gmx.de> wrote:
> > It seems to me that the RFC is not sufficiently specific enough > regarding the concatenation of instances of classes which implement > __toString().
Exactly what I was thinking too. Would be nice with some examples on this.
> So if we ever wanted to change the value of > PHP_OPERAND_TYPES_NOT_SUPPORTED, we'd introduce a BC break. Therefore > the constant's value likely will never change, so using NULL directly to > signal unsupported operand types would be fine, wouldn't it?
Agree on this too.
  109340
March 26, 2020 16:37 jan.h.boehmer@gmx.de
On Thursday, March 26, 2020 3:50 AM Jakob Givoni <jakob@givoni.dk> wrote:

> On Wed, Mar 25, 2020 at 6:28 AM Christoph M. Becker <cmbecker69@gmx.de> wrote: > >> It seems to me that the RFC is not sufficiently specific enough >> regarding the concatenation of instances of classes which implement >> __toString(). > > Exactly what I was thinking too. Would be nice with some examples on this.
I am not sure if I can (or should) still add this to the RFC, here some clarification on this: The overloaded concat operator has higher priority than the __toString() method. So if Class A overloades the concat operator, then calling $a . $b means ClassA::__concat($a, $b); (Note that both operands are passed in their original form) If you want to concat the string representations, you will have to explicitly convert the objects to strings: $ret = (string) $a . (string) $b; If the concat operator is not overloaded, the behavior is like now, and the objects are converted implicitly to strings (so $a . $b actually means (string) $a . (string) $b). Furthermore an notice is triggered, hinting the user that he could overload the concat operator. (Maybe here a different message than for the other operators would be useful).
  109345
March 26, 2020 17:18 guilliam.xavier@gmail.com (Guilliam Xavier)
On Thu, Mar 26, 2020 at 5:37 PM boehmer@gmx.de> wrote:
> > The overloaded concat operator has higher priority than the __toString() method. > So if Class A overloades the concat operator, then calling $a . $b means ClassA::__concat($a, $b); (Note that both operands are passed in their original form) > If you want to concat the string representations, you will have to explicitly convert the objects to strings: > $ret = (string) $a . (string) $b;
This first part seems legit to me.
> If the concat operator is not overloaded, the behavior is like now, and the objects are converted implicitly to strings (so $a . $b actually means (string) $a . (string) $b). > Furthermore an notice is triggered, hinting the user that he could overload the concat operator. (Maybe here a different message than for the other operators would be useful).
I fear that "hint" notice could break Symfony apps... Couldn't you just not trigger it in this case? -- Guilliam Xavier
  109346
March 26, 2020 17:25 cmbecker69@gmx.de ("Christoph M. Becker")
On 26.03.2020 at 18:18, Guilliam Xavier wrote:

> On Thu, Mar 26, 2020 at 5:37 PM boehmer@gmx.de> wrote: >> >> The overloaded concat operator has higher priority than the __toString() method. >> So if Class A overloades the concat operator, then calling $a . $b means ClassA::__concat($a, $b); (Note that both operands are passed in their original form) >> If you want to concat the string representations, you will have to explicitly convert the objects to strings: >> $ret = (string) $a . (string) $b; > > This first part seems legit to me. > >> If the concat operator is not overloaded, the behavior is like now, and the objects are converted implicitly to strings (so $a . $b actually means (string) $a . (string) $b). >> Furthermore an notice is triggered, hinting the user that he could overload the concat operator. (Maybe here a different message than for the other operators would be useful). > > I fear that "hint" notice could break Symfony apps... Couldn't you > just not trigger it in this case?
Either that (it's what I would prefer), or clearly document that BC break (even if it's just about a notice). Thanks, Christoph
  109347
March 26, 2020 17:36 jan.h.boehmer@gmx.de
On Thursday, March 26, 2020 6:19 PM Guilliam Xavier xavier@gmail.com> wrote:

>> If the concat operator is not overloaded, the behavior is like now, and the objects are converted implicitly to strings (so $a . $b actually means (string) $a . (string) $b). >> Furthermore an notice is triggered, hinting the user that he could overload the concat operator. (Maybe here a different message than for the other operators would be useful). > > I fear that "hint" notice could break Symfony apps... Couldn't you just not trigger it in this case?
Yes this would be possible and I think that it might be reasonable to omit the notice in this case (maybe only if the objects really implement a __toString). What do others think about this? Regards, Jan
  109290
March 25, 2020 12:25 lisachenko.it@gmail.com (Alexander Lisachenko)
Hi, internals!

I want to mention, that all existing internal API of Zend could be
accessible via FFI as of PHP7.4. This gives opportunity to implement
userspace operator overloading as a simple PHP package.

I know, that FFI requires some polishing, but it could become a tool to
create interesting extensions via Z-Engine or similar libraries. Just for
example, I have a repository
https://github.com/lisachenko/native-types which provides an example of
userspace operator-overloading for matrices (see __doOperation method
implementation at
https://github.com/lisachenko/native-types/blob/master/src/Matrix.php#L232-L276
)

Please share you thoughts about this.

Best regards,
Alexander

пн, 23 мар. 2020 г. в 20:58, boehmer@gmx.de>:

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users > to overload operators in their own classes. > > Voting closes on 2020-04-06. > > Regards, > Jan Böhmer > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  109342
March 26, 2020 16:52 derick@php.net (Derick Rethans)
On Mon, 23 Mar 2020, jan.h.boehmer@gmx.de wrote:

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows users > to overload operators in their own classes.
I've always been a strong proponent of operator overloading, but as PHP already has it internally, it makes sense to also make that information available for userland. cheers, Derick
  109349
March 26, 2020 18:06 derick@php.net (Derick Rethans)
On Thu, 26 Mar 2020, Derick Rethans wrote:

> On Mon, 23 Mar 2020, jan.h.boehmer@gmx.de wrote: > > > I have opened voting on > > https://wiki.php.net/rfc/userspace_operator_overloading, which allows users > > to overload operators in their own classes. > > I've always been a strong proponent of operator overloading, but as PHP ^^^^^^^^^
I meant "opponent" there of course.
> already has it internally, it makes sense to also make that information > available for userland.
In any case, I am a +1! cheers, Derick -- PHP 7.4 Release Manager Host of PHP Internals News: https://phpinternals.news Like Xdebug? Consider supporting me: https://xdebug.org/support https://derickrethans.nl | https://xdebug.org | https://dram.io twitter: @derickr and @xdebug
  109352
March 26, 2020 19:02 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
pon., 23 mar 2020 o 18:58 boehmer@gmx.de> napisał(a):

> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users > to overload operators in their own classes. >
I got two comments, a little late but always better than even later. The first thing is for operator methods when the operation is not supported I would see simply `return null;` as the right solution instead of constant, which name no one will remember. The second thing is notices which are confusing when suggesting to go and implement overloaded operator method on extension derived classes like stdClass like below: Notice: You have to implement the __add function in class stdClass to use this operator with an object in... The text of the notice is not documented in the RFC but it is implemented that way in the patch. A PHP developer is not likely gonna download the php-src source code, build environment and go with the ext/standard implementation and start adding it in C. The notice in this cases should either be different or not emitted at all. If the latter then that has to be documented I guess. Cheers, -- Michał Brzuchalski
  109384
March 28, 2020 13:13 ajf@ajf.me (Andrea Faulds)
Hi,

Michał Brzuchalski wrote:
> The first thing is for operator methods when the operation is not supported > I would see simply `return null;` as the right solution > instead of constant, which name no one will remember.
If we are to allow arbitrary use of operator overloading (rather than restricting it to specific kinds of use), which this RFC does, then IMHO we shouldn't special-case “null” — why can't it be a valid result of an overloaded operator? It is an arbitrary constraint on what can be done with operator overloading and makes the feature less generic. I would prefer if we used a different mechanism, for example a special subclass of Exception which will be caught internally by the operator overloading code, so that signalling an operation is unsupported can be “out of band” if that makes sense.
> The second thing is notices which are confusing when suggesting to go > and implement overloaded operator method on extension derived classes > like stdClass like below: > > Notice: You have to implement the __add function in class stdClass to use > this operator with an object in... > > The text of the notice is not documented in the RFC but it is implemented > that way in the patch.
Please document all notices your patch produces in the RFC, I think they are an important detail of the proposal.
> A PHP developer is not likely gonna download the php-src source code, > build environment and go with the ext/standard implementation and start > adding it in C. > The notice in this cases should either be different or not emitted at all. > If the latter then that has to be documented I guess.
That sounds reasonable, and it should be simple to add a check to your patch to make sure it won't show this message for internal/extension classes, right? By the way, if the operation has two operands, one of which is an internal/extension class and the other a user class, you could perhaps still show the mssage, but only mention the user class name. Regars, Andrea
  109376
March 27, 2020 20:10 johannes@schlueters.de (Johannes =?ISO-8859-1?Q?Schl=FCter?=)
On Mon, 2020-03-23 at 18:58 +0100, jan.h.boehmer@gmx.de wrote:
> Hi internals, > > I have opened voting on > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > users to overload operators in their own classes. >
I consider operator overlaoding in general a good feature (while PHP's overload for + on arrays is weird) However I don't like this design. Consider this case: Some library provides a type A without overloads. I then create a type B with overload: class B { static public __add($lhs, $rhs) { if ($lhs instanceof A) ... elseif($rhs instanceof A) ... else error } } as I want my type's addition to commutative I handle this with some if (probably dispatching. Now I can invoke it: new A() + new B(); // calls B::__add() new B() + new A(); // calls B::__add() as well Now the maintainer of A implements the feature request "A should allow addition with integers", I update my library and suddenly weird things happen. new A() + new B(); // calls A::__add() and explodes, as A doesn't know B new B() + new A(); // calls B::__add() works as before Now we could establish the best practice that one type's operator calls the other type's operator in case it can't handle a type. However that is no good option as it will lead to infinite recursion if neither type can handle both operands. The issue is that by forcing type declarations as part of the class, as members, this forms a closed set, but for many cases a strict closed set isn't what one wants. For mathematical types having operators working with integers (and other numeric values) is essential. Communativity often also is required. So a true closed set doesn't work, for an open set can't be created easily in today's PHP. Thus the RFC tries to tie this together, but this will fall apart and then cause legacy and BC reasons preventing future improvement. I believe the pre-requisit is having some form of function overloading, where operator functions for specific argument types can be defined. In https://news-web.php.net/php.internals/108425 Andrea created an idea, which is probably "ugly" but has less usage restrictions. I think spending time on function overloading (I believe, without proving it, this can be done with very little cost for non-overlaoded cases - by adding a flag "overloaded" along visibility flags and check that along with the visibility check, only in case of an overload to the "expensive" check, which still is cheaper done in the engine than if/else chains in userspace) and then take up operator overloading again, rather than this smart but limited approach. (For whoever does that: spend time in C++ and its function resolution rules incl. ADL, not to copy, but to learn) johannes
  109379
March 28, 2020 09:22 arnold.adaniels.nl@gmail.com (Arnold Daniels)
> > > On Mon, 2020-03-23 at 18:58 +0100, jan.h.boehmer@gmx.de wrote: > > Hi internals, > > > > I have opened voting on > > https://wiki.php.net/rfc/userspace_operator_overloading, which allows > > users to overload operators in their own classes. > > > > I consider operator overlaoding in general a good feature (while PHP's > overload for + on arrays is weird) > > However I don't like this design. > > Consider this case: Some library provides a type A without overloads. I > then create a type B with overload: > > class B { > static public __add($lhs, $rhs) { > if ($lhs instanceof A) ... > elseif($rhs instanceof A) ... > else error > } > } > > as I want my type's addition to commutative I handle this with some if > (probably dispatching. Now I can invoke it: > > new A() + new B(); // calls B::__add() > new B() + new A(); // calls B::__add() as well > > Now the maintainer of A implements the feature request "A should allow > addition with integers", I update my library and suddenly weird things > happen. > > new A() + new B(); // calls A::__add() and explodes, as A doesn't know > B > new B() + new A(); // calls B::__add() works as before > > Now we could establish the best practice that one type's operator calls > the other type's operator in case it can't handle a type. However that > is no good option as it will lead to infinite recursion if neither type > can handle both operands. > > > The issue is that by forcing type declarations as part of the class, as > members, this forms a closed set, but for many cases a strict closed > set isn't what one wants. For mathematical types having operators > working with integers (and other numeric values) is essential. > Communativity often also is required. So a true closed set doesn't > work, for an open set can't be created easily in today's PHP. Thus the > RFC tries to tie this together, but this will fall apart and then cause > legacy and BC reasons preventing future improvement. > > I believe the pre-requisit is having some form of function overloading, > where operator functions for specific argument types can be defined. In > https://news-web.php.net/php.internals/108425 Andrea created an idea, > which is probably "ugly" but has less usage restrictions. I think > spending time on function overloading (I believe, without proving it, > this can be done with very little cost for non-overlaoded cases - by > adding a flag "overloaded" along visibility flags and check that along > with the visibility check, only in case of an overload to the > "expensive" check, which still is cheaper done in the engine than > if/else chains in userspace) and then take up operator overloading > again, rather than this smart but limited approach. (For whoever does > that: spend time in C++ and its function resolution rules incl. ADL, > not to copy, but to learn) > > johannes > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > This issues become even more apparent when sequencing operations like `$a +
$b + $c - $d`. Trying left, then trying right, will make it very difficult to determine the outcome of such a statement. The arguments against type hinting for operator methods, assume the "try left/right" method. Instead, type hinting should be applied to determine which method should be used. If both or neither methods are applicable, an error must be thrown. With class inheritance, you also don't want to rely on the order of the operands. If you're the creator of a library, that's out of your hands. If both the parent and child class overwrite the operation, and both accept the operands, then the child class should always be used. It's a -1 for me. I would vote in favor of an RFC for operator overloading without the "try left, try right" behavior. Arnold
  109382
March 28, 2020 12:25 cmbecker69@gmx.de ("Christoph M. Becker")
On 28.03.2020 at 10:22, Arnold Daniels wrote:

> This issues become even more apparent when sequencing operations like `$a + > $b + $c - $d`. Trying left, then trying right, will make it very difficult > to determine the outcome of such a statement. > > The arguments against type hinting for operator methods, assume the "try > left/right" method. Instead, type hinting should be applied to determine > which method should be used. If both or neither methods are applicable, an > error must be thrown.
This "try left/right" approach is how operator overloading works for internal classes[1], and apparently, it works quite well, as long as it is not overused.
> With class inheritance, you also don't want to rely on the order of the > operands. If you're the creator of a library, that's out of your hands. If > both the parent and child class overwrite the operation, and both accept > the operands, then the child class should always be used.
If a subclass overrides the operation, it would have to conform to parameter type contravariance, so it would always be used, anyway. [1] <https://github.com/php/php-src/blob/php-7.4.4/Zend/zend_operators.h#L934-L937> -- Christoph M. Becker
  109387
March 28, 2020 13:29 ajf@ajf.me (Andrea Faulds)
Hi,

Christoph M. Becker wrote:
> On 28.03.2020 at 10:22, Arnold Daniels wrote: > >> This issues become even more apparent when sequencing operations like `$a + >> $b + $c - $d`. Trying left, then trying right, will make it very difficult >> to determine the outcome of such a statement. >> >> The arguments against type hinting for operator methods, assume the "try >> left/right" method. Instead, type hinting should be applied to determine >> which method should be used. If both or neither methods are applicable, an >> error must be thrown. > > This "try left/right" approach is how operator overloading works for > internal classes[1], and apparently, it works quite well, as long as it > is not overused.
I think “as long as it is not overused” are the key words there. We have a very limited number of internal classes with operator overloading right now, and the authors know about what other such classes exist and therefore can design with them in mind. But once any userland PHP class can use operator overloading, you could start seeing uses of operators where the left- and right-hand side are classes from unrelated libraries whose authors were not aware of eachother, and that could create strange problems when one or the other library is updated with operator overload handling. Consider Arnold's example: $a + $b + $c - $d We are not constraining operator overloading to just be for number-like objects, it can in principle be used for absolutely anything. $a here might have an overload that works for any right-hand-side object, but then once $b adds its own similar overload in a new version, the code no longer does the same thing when it's flipped around ($b + $a), violating the normal expectation that `+` is commutative, and that's before considering what $c and $d might do! Thanks, Andrea
  109392
March 28, 2020 15:16 cmbecker69@gmx.de ("Christoph M. Becker")
On 28.03.2020 at 14:29, Andrea Faulds wrote:

> Christoph M. Becker wrote: > >> This "try left/right" approach is how operator overloading works for >> internal classes[1], and apparently, it works quite well, as long as it >> is not overused. > > I think “as long as it is not overused” are the key words there. We have > a very limited number of internal classes with operator overloading > right now, and the authors know about what other such classes exist and > therefore can design with them in mind. But once any userland PHP class > can use operator overloading, you could start seeing uses of operators > where the left- and right-hand side are classes from unrelated libraries > whose authors were not aware of eachother, and that could create strange > problems when one or the other library is updated with operator overload > handling. > > Consider Arnold's example: > >     $a + $b + $c - $d > > We are not constraining operator overloading to just be for number-like > objects, it can in principle be used for absolutely anything.
And floats can be used to store monetary values, and to do calculations, and apparently, that is actually done by some. Was it a bad idea to introduce floats to the language?
> $a here > might have an overload that works for any right-hand-side object, but > then once $b adds its own similar overload in a new version, the code no > longer does the same thing when it's flipped around ($b + $a), violating > the normal expectation that `+` is commutative, and that's before > considering what $c and $d might do!
If, for example, the + operator is overloaded to add something to a collection, the normal expectation that + is commutative is already violated. Operator overloading should definitely not be used for "anything", but only for those rare cases which resemble math operations (frankly, I would not have not supported __concat() at all). Also, overloaded operators should be programmed defensively, i.e. they should not accept arbitrary arguments (how could that even work?), but only those they can handle. If implementations adhere to these "rules", I don't see real issues. -- Christoph M. Becker
  109404
March 28, 2020 21:21 ajf@ajf.me (Andrea Faulds)
Hi,

Christoph M. Becker wrote:
> (frankly, I would not have not supported __concat() at all). Also, > overloaded operators should be programmed defensively, i.e. they should > not accept arbitrary arguments (how could that even work?), but only > those they can handle. If implementations adhere to these "rules", I > don't see real issues.
Consider a type implementing some kind of list. Perhaps someone would want to overload the + operator to mean adding an item to the list. If the list accepts any type of value as a valid item, then you have an example of an unconditional overload. Regards, Andrea
  109412
March 29, 2020 10:52 cmbecker69@gmx.de ("Christoph M. Becker")
On 28.03.2020 at 22:21, Andrea Faulds wrote:

> Christoph M. Becker wrote: >> (frankly, I would not have not supported __concat() at all).  Also, >> overloaded operators should be programmed defensively, i.e. they should >> not accept arbitrary arguments (how could that even work?), but only >> those they can handle.  If implementations adhere to these "rules", I >> don't see real issues. > > Consider a type implementing some kind of list. Perhaps someone would > want to overload the + operator to mean adding an item to the list. If > the list accepts any type of value as a valid item, then you have an > example of an unconditional overload.
I had already considered this, and wrote immediately above the quote:
> If, for example, the + operator is overloaded to add something to a > collection, the normal expectation that + is commutative is already > violated. Operator overloading should definitely not be used for > "anything", but only for those rare cases which resemble math > operations
Thanks, Christoph
  109391
March 28, 2020 14:57 johannes@schlueters.de (=?ISO-8859-1?Q?Johannes_Schl=FCter?=)
On March 28, 2020 1:25:11 PM GMT+01:00, "Christoph M. Becker" <cmbecker69@gmx.de> wrote:
>On 28.03.2020 at 10:22, Arnold Daniels wrote: > >> This issues become even more apparent when sequencing operations like >`$a + >> $b + $c - $d`. Trying left, then trying right, will make it very >difficult >> to determine the outcome of such a statement. >> >> The arguments against type hinting for operator methods, assume the >"try >> left/right" method. Instead, type hinting should be applied to >determine >> which method should be used. If both or neither methods are >applicable, an >> error must be thrown. > >This "try left/right" approach is how operator overloading works for >internal classes[1], and apparently, it works quite well, as long as it >is not overused.
The fact that it works in one or two cases as an implementation detail where the full implementation is controlled by a single group (internals) is no indication for it to work at large.
>> With class inheritance, you also don't want to rely on the order of >>the
Inheritance is always complex. Yes operator handling (especially if type dispatching happens manually) will add a few things to think about. Certainly an interesting field to explore, before one votes. I myself consider larger inheritance structures a thing of the 90ies and for special cases like GUI widgets ... but yeah, language design has to consider it. Changing languages features like discussed here in future is hard. johannes
  109393
March 28, 2020 16:05 cmbecker69@gmx.de ("Christoph M. Becker")
On 28.03.2020 at 15:57, Johannes Schlüter wrote:

> On March 28, 2020 1:25:11 PM GMT+01:00, "Christoph M. Becker" <cmbecker69@gmx.de> wrote: > >> This "try left/right" approach is how operator overloading works for >> internal classes[1], and apparently, it works quite well, as long as it >> is not overused. > > The fact that it works in one or two cases as an implementation detail where the full implementation is controlled by a single group (internals) is no indication for it to work at large.
Fair enough. But maybe Python, where userland operator overloading works similar to the proposal at hand, is? :) Furthermore, it is already possible to do operator overloading in userland by using the FFI extension[1], and given that operator overloading appears to be desired by many developers, I'd rather add it to the language, then having to deal with libraries which do it via FFI. [1] <https://github.com/lisachenko/z-engine#object-extensions-api> -- Christoph M. Becker
  109395
March 28, 2020 16:27 johannes@schlueters.de (Johannes =?ISO-8859-1?Q?Schl=FCter?=)
On Sat, 2020-03-28 at 17:05 +0100, Christoph M. Becker wrote:
> On 28.03.2020 at 15:57, Johannes Schlüter wrote: > > > On March 28, 2020 1:25:11 PM GMT+01:00, "Christoph M. Becker" < > > cmbecker69@gmx.de> wrote: > > > > > This "try left/right" approach is how operator overloading works > > > for > > > internal classes[1], and apparently, it works quite well, as long > > > as it > > > is not overused. > > > > The fact that it works in one or two cases as an implementation > > detail where the full implementation is controlled by a single > > group (internals) is no indication for it to work at large. > > Fair enough. But maybe Python, where userland operator overloading > works similar to the proposal at hand, is? :)
It doesn't: Python 3.6.9 (default, Nov 7 2019, 10:44:02) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.
>>> class A: .... def __add__(self, other):
.... print("add") ....
>>> a = A() >>> a + 1 add
>>> 1 + a Traceback (most recent call last):
File "", line 1, in TypeError: unsupported operand type(s) for +: 'int' and 'A' johannes
  109396
March 28, 2020 16:44 nikita.ppv@gmail.com (Nikita Popov)
On Sat, Mar 28, 2020 at 5:28 PM Johannes Schlüter <johannes@schlueters..de>
wrote:

> On Sat, 2020-03-28 at 17:05 +0100, Christoph M. Becker wrote: > > On 28.03.2020 at 15:57, Johannes Schlüter wrote: > > > > > On March 28, 2020 1:25:11 PM GMT+01:00, "Christoph M. Becker" < > > > cmbecker69@gmx.de> wrote: > > > > > > > This "try left/right" approach is how operator overloading works > > > > for > > > > internal classes[1], and apparently, it works quite well, as long > > > > as it > > > > is not overused. > > > > > > The fact that it works in one or two cases as an implementation > > > detail where the full implementation is controlled by a single > > > group (internals) is no indication for it to work at large. > > > > Fair enough. But maybe Python, where userland operator overloading > > works similar to the proposal at hand, is? :) > > It doesn't: > > > Python 3.6.9 (default, Nov 7 2019, 10:44:02) > [GCC 8.3.0] on linux > Type "help", "copyright", "credits" or "license" for more information. > >>> class A: > ... def __add__(self, other): > ... print("add") > ... > >>> a = A() > >>> a + 1 > add > >>> 1 + a > Traceback (most recent call last): > File "", line 1, in > TypeError: unsupported operand type(s) for +: 'int' and 'A' >
It does. Because Python uses instance methods for operator overloading, it has to use pairs of methods like __add__ and __radd__ to handle commuted variants. If you want to implement a commutative add, you need to implement both __add__ and __radd__. If __add__ is not implemented or returns NotImplemented, then __radd__ will be called. So, as Christoph has said, this is indeed the same system as what is being proposed here, with the difference that we do not need two separate methods per operator, but can use a static method instead. Regards, Nikita
  109397
March 28, 2020 17:13 johannes@schlueters.de (=?ISO-8859-1?Q?Johannes_Schl=FCter?=)
On March 28, 2020 5:44:28 PM GMT+01:00, Nikita Popov ppv@gmail.com> wrote:
>On Sat, Mar 28, 2020 at 5:28 PM Johannes Schlüter ><johannes@schlueters.de> >wrote: > >> On Sat, 2020-03-28 at 17:05 +0100, Christoph M. Becker wrote: >> > On 28.03.2020 at 15:57, Johannes Schlüter wrote: >> > >> > > On March 28, 2020 1:25:11 PM GMT+01:00, "Christoph M. Becker" < >> > > cmbecker69@gmx.de> wrote: >> > > >> > > > This "try left/right" approach is how operator overloading >works >> > > > for >> > > > internal classes[1], and apparently, it works quite well, as >long >> > > > as it >> > > > is not overused. >> > > >> > > The fact that it works in one or two cases as an implementation >> > > detail where the full implementation is controlled by a single >> > > group (internals) is no indication for it to work at large. >> > >> > Fair enough. But maybe Python, where userland operator overloading >> > works similar to the proposal at hand, is? :) >> >> It doesn't: >> >> >> Python 3.6.9 (default, Nov 7 2019, 10:44:02) >> [GCC 8.3.0] on linux >> Type "help", "copyright", "credits" or "license" for more >information. >> >>> class A: >> ... def __add__(self, other): >> ... print("add") >> ... >> >>> a = A() >> >>> a + 1 >> add >> >>> 1 + a >> Traceback (most recent call last): >> File "", line 1, in >> TypeError: unsupported operand type(s) for +: 'int' and 'A' >> > >It does. > >Because Python uses instance methods for operator overloading, it has >to >use pairs of methods like __add__ and __radd__ to handle commuted >variants. >If you want to implement a commutative add, you need to implement both >__add__ and __radd__. If __add__ is not implemented or returns >NotImplemented, then __radd__ will be called. > >So, as Christoph has said, this is indeed the same system as what is >being >proposed here, with the difference that we do not need two separate >methods >per operator, but can use a static method instead.
If we use two methods as well it is a saner design. I won't like it, but lot better than the current one. johannes
  109403
March 28, 2020 21:18 ajf@ajf.me (Andrea Faulds)
Hi everyone,

Johannes Schlüter wrote:
> > If we use two methods as well it is a saner design. I won't like it, but lot better than the current one.
Just want to +1 this. Two methods, neither of which are static, seems like a cleaner approach to me. I maybe like this better than my suggested boolean argument (it certainly looks nicer). Consider what happens in the current proposal if a class, intentionally or otherwise, only supports being on one side of an operator… it will produce the same error message as if it didn't support the overload at all :( Thanks, Andrea
  109544
April 6, 2020 19:36 jan.h.boehmer@gmx.de
Hi internals,

I have closed the voting. With 38 in favor and 28 against the RFC is DECLINED (didn’t reach the needed 2/3 majority for a new feature).

Thanks to everyone who has participated.
  109545
April 6, 2020 19:57 pollita@php.net (Sara Golemon)
On Mon, Apr 6, 2020 at 2:36 PM boehmer@gmx.de> wrote:

> I have closed the voting. With 38 in favor and 28 against the RFC is > DECLINED (didn’t reach the needed 2/3 majority for a new feature).. > > Please don't be discouraged. I did vote against, but I was on the fence (My vote was 'Yes' for a period). Review the feedback you were offered,
revise the proposal, perhaps even shrink the scope if that seems appropriate. At 38:28 I don't think we're far off of a version of this feature which meets with success. -Sara
  109579
April 9, 2020 12:18 Danack@basereality.com (Dan Ackroyd)
On Mon, 6 Apr 2020 at 20:36, boehmer@gmx.de> wrote:
> > Hi internals, > > I have closed the voting. With 38 in favor and 28 against the RFC is DECLINED (didn’t reach the needed 2/3 majority for a new feature). > > Thanks to everyone who has participated. >
Hi Jan, Thanks for running the RFC. Although it didn't quite pass it seems a lot closer now. Apologies for not taking part in the discussion earlier, but I'm slow at thinking. To follow up on a couple of things.
> If it can not handle the given type, it has to return the > constant PHP_OPERAND_TYPES_NOT_SUPPORTED (currently just null).
This does not appear to be a good choice. It means that to evaluate whether the code is safe to call the code needs to be evaluated. That makes doing static code analysis very difficult. Also, it means that operators could not return null as a result of the operation, which seems like an odd choice that would be a regretful limitation. Rowan Tommins wrote from https://externals.io/message/108788#108993 :
> > > $a = new A; > $b = new B; > var_dump($b + $a); # calls B::__add($b, $a); OK > var_dump($a + $b); # calls A::__add($a, $b), which is a TypeError
And jan.h.boehmer@gmx.de wrote:
> > If an operator handler has typehints, an error will be thrown, > What do others think about this restriction?
I think that is the wrong solution to the wrong problem. It's a wrong solution because parameter types* are a useful tool both for program correctness at runtime, they make it easier to run static code analysis tools on the code and most importantly they save a lot of developer time, mostly through autocomplete. It's the wrong problem because that code does have a TypeError. It is calling '__add' and passing a parameter to the method that the code can't handle. It appears to be the same error case as: ``` class A { public function add(A $lhs, A $rhs) {...} } class B { public function add(A|B $lhs, A|B $rhs) {...} } $a = new A; $b = new B; $b->add($a); // Ok $a->add($b); // TypeError ``` For me, the solution to the error in this code is not "remove the parameter type on A::add". I'm pretty sure that the solution to this type of error would be "fix your code". When someone looks at the idea of userspace operator overloading again, I think the following path might reach a more acceptable solution. * Encourage people to use parameter types to make their code easier to reason about. * Encourage people to use static analysis to check their code for errors, including errors where they have invalid operands to operations. * Take the approach in the RFC that passing a wrong parameter type to an operator magic method represents a programming error that cannot be handled in the normal program flow, and that throwing an exception is the right thing to do here**. But as most people should be using parameter types, and static analyzers to check for this type of error, this should not occur. Other than a learned reticence against throwing exceptions, what downsides would that have? cheers Dan Ack * PHP has parameter types that are checked at runtime, not 'hints' that can be worked around as found in other programming languages. If people would stop using the phrase 'type hint', it would make the conversation more accurate. ** This is not using exceptions for flow control (though it could be abused for it). Most people should be avoiding these problems through writing code correctly, and using static analysis to double-check their code is free from errors.
  109580
April 9, 2020 12:41 Danack@basereality.com (Dan Ackroyd)
On Thu, 9 Apr 2020 at 13:18, Dan Ackroyd <Danack@basereality.com> wrote:

> It appears to be the same error case as: >
And that code had a mistake. Should have been: ``` class A { public function add(A $rhs) {...} } class B { public function add(A|B $rhs) {...} } $a = new A; $b = new B; $b->add($a); // Ok $a->add($b); // TypeError ``` cheers Dan Ack
  109583
April 9, 2020 13:02 cmbecker69@gmx.de ("Christoph M. Becker")
On 09.04.2020 at 14:41, Dan Ackroyd wrote:

> On Thu, 9 Apr 2020 at 13:18, Dan Ackroyd <Danack@basereality.com> wrote: > >> It appears to be the same error case as: >> > > And that code had a mistake. Should have been: > > ``` > class A { > public function add(A $rhs) {...} > } > > class B { > public function add(A|B $rhs) {...} > } > > $a = new A; > $b = new B; > > $b->add($a); // Ok > $a->add($b); // TypeError > ```
If we'd go with (dynamic) methods, wouldn't we have to add "r"-methods (like Python does) as well? And if we did so, wouldn't we encourage misusing operator overloading for all kinds of stuff (for which it shouldn't be used, in my opinion)? -- Christoph M. Becker
  109584
April 9, 2020 15:05 rowan.collins@gmail.com (Rowan Tommins)
On Thu, 9 Apr 2020 at 13:18 (and subsequent correction), Dan Ackroyd <
Danack@basereality.com> wrote:

> > $a = new A; > > $b = new B; > > var_dump($b + $a); # calls B::__add($b, $a); OK > > var_dump($a + $b); # calls A::__add($a, $b), which is a TypeError > > > ... that code does have a TypeError. It is > calling '__add' and passing a parameter to the method that the code > can't handle. > > It appears to be the same error case as: > > ``` > class A { > public function add(A $rhs) {...} > } > > class B { > public function add(A|B $rhs) {...} > } > > $a = new A; > $b = new B; > > $b->add($a); // Ok > $a->add($b); // TypeError > ``` >
As with so much else on this topic, it depends how you think about operator overloading - and I think that's why it's so hard to agree on an implementation. It seems that you're picturing the overloaded + as like a normal method but with special syntax, so that $a + $b means the same as $a->add($b) and only that. In that interpretation, it's perfectly reasonable to have the operation succeed or fail based only on the left-hand operand, because that's how we're used to method dispatch working. But if you look at how "normal" operators work, it's far less obvious that the order of operands should play any role in that decision. For instance, when mixing float and int, the result is a float if *either* of the operands is a float: var_dump(1 + 1); # int(2) var_dump(1 + 1.0); # float(2) var_dump(1.0 + 1); # float(2) var_dump(1.0 + 1.0); # float(2) Substitute 1 for $a and 1.0 for $b, and you're back to the example I originally wrote. Note that this is true even for non-commutative operators like exponentiation: var_dump(2 ** 3); # int(8) var_dump(2 ** 3.0); # float(8) var_dump(2.0 ** 3); # float(8) var_dump(2.0 ** 3.0); # float(8) My impression is what people consider "good" use of operator overloading is much closer to "make things act like built in numerics" than "make operators with fancy syntax", so some form of symmetry is necessary I think. Regards, -- Rowan Tommins [IMSoP]
  109585
April 9, 2020 18:57 larry@garfieldtech.com ("Larry Garfield")
On Thu, Apr 9, 2020, at 10:05 AM, Rowan Tommins wrote:
> On Thu, 9 Apr 2020 at 13:18 (and subsequent correction), Dan Ackroyd < > Danack@basereality.com> wrote: > > > > $a = new A; > > > $b = new B; > > > var_dump($b + $a); # calls B::__add($b, $a); OK > > > var_dump($a + $b); # calls A::__add($a, $b), which is a TypeError > > > > > > ... that code does have a TypeError. It is > > calling '__add' and passing a parameter to the method that the code > > can't handle. > > > > It appears to be the same error case as: > > > > ``` > > class A { > > public function add(A $rhs) {...} > > } > > > > class B { > > public function add(A|B $rhs) {...} > > } > > > > $a = new A; > > $b = new B; > > > > $b->add($a); // Ok > > $a->add($b); // TypeError > > ``` > > > > > As with so much else on this topic, it depends how you think about operator > overloading - and I think that's why it's so hard to agree on an > implementation. > > It seems that you're picturing the overloaded + as like a normal method but > with special syntax, so that $a + $b means the same as $a->add($b) and only > that. In that interpretation, it's perfectly reasonable to have the > operation succeed or fail based only on the left-hand operand, because > that's how we're used to method dispatch working. > > But if you look at how "normal" operators work, it's far less obvious that > the order of operands should play any role in that decision. For instance, > when mixing float and int, the result is a float if *either* of the > operands is a float: > > var_dump(1 + 1); # int(2) > var_dump(1 + 1.0); # float(2) > var_dump(1.0 + 1); # float(2) > var_dump(1.0 + 1.0); # float(2) > > Substitute 1 for $a and 1.0 for $b, and you're back to the example I > originally wrote. Note that this is true even for non-commutative operators > like exponentiation: > > var_dump(2 ** 3); # int(8) > var_dump(2 ** 3.0); # float(8) > var_dump(2.0 ** 3); # float(8) > var_dump(2.0 ** 3.0); # float(8) > > My impression is what people consider "good" use of operator overloading is > much closer to "make things act like built in numerics" than "make > operators with fancy syntax", so some form of symmetry is necessary I think. > > Regards, > -- > Rowan Tommins > [IMSoP]
Idle, possibly naive thought: When applying operator overloading to objects, perhaps we could simplify matters by insisting that it only work for directly compatible types? That is: class Foo { public function __add(Foo $b): Foo { return new FooOrChildOfFood(); } } It would work for interfaces too, but the point is that you *can't* operate on just any old other value... only one that is of the same type. You could technically have a BarInterface, and then only the left-side object gets called, but it means it has to be combining two BarInterface objects, which means it knows how, because BarInterface has the necessary methods. If not, then your BarInterface is wrong and you should feel bad. This creates a narrower use case, but perhaps one that still fits the practical usage patterns? (Eg, adding Money objects together, etc.) If an operator by design wants different types on each side (not for numeric behavior, but for, eg, a function concatenation operator), then you would have to type the RHS you expect (eg, a callable in that case). It's not as extensible, but the extensibility seems like it's the problem in the first place. I generally agree with those who have said that any such functionality needs to leverage the type system effectively, not side-step it. As I said, possibly naive thought, but could deliberately reducing the scope make life simpler? --Larry Garfield
  109385
March 28, 2020 13:19 sebastian@php.net (Sebastian Bergmann)
Am 27.03.2020 um 21:10 schrieb Johannes Schlüter:
> However I don't like this design. > [...] Changed my vote back to "no" based on Johannes' arguments.
  109388
March 28, 2020 13:43 ajf@ajf.me (Andrea Faulds)
Hi Johannes,

Johannes Schlüter wrote:
> I believe the pre-requisit is having some form of function overloading, > where operator functions for specific argument types can be defined. In > https://news-web.php.net/php.internals/108425 Andrea created an idea, > which is probably "ugly" but has less usage restrictions. I think > spending time on function overloading (I believe, without proving it, > this can be done with very little cost for non-overlaoded cases - by > adding a flag "overloaded" along visibility flags and check that along > with the visibility check, only in case of an overload to the > "expensive" check, which still is cheaper done in the engine than > if/else chains in userspace) and then take up operator overloading > again, rather than this smart but limited approach. (For whoever does > that: spend time in C++ and its function resolution rules incl. ADL, > not to copy, but to learn)
Thinking about what I suggested there again now, I think it's a shame this RFC includes the following restriction: > The argument must not specify any argument typehints (an error is thrown otherwise), as typehints and occuring type errors would break operator evaluation (see discussion). With strict evaluation of union types, it would be possible to achieve something like what I suggested in a less ugly form: class A { /* … */ public function __add(int|float|B $other) { /* … */ } } class B { /* … */ public function __add(int|C $other) { /* … */ } } From the union typs in this example, the interpreter could see, without having to execute either overloaded method, that A provides an overload for B, int and float, and B provides an overload for C and int, and so it knows the same method can be called for both `$a + $b` and `$b + $a`, likewise for `$b + $c` and `$c + $b`. Of course, this requires interpreting type declarations in an unusual way, and it also needs a solution to non-commutative operators (perhaps a second parameter that would contain a boolean specifying whether `$this` is on the left), but I think it is less likely to be chaotic than the current proposal. Thanks, Andrea