Re: [PHP-DEV] Operator overloading for userspace objects

This is only part of a thread. view whole thread
  108311
January 29, 2020 09:20 nikita.ppv@gmail.com (Nikita Popov)
On Wed, Jan 29, 2020 at 12:14 AM boehmer@gmx.de> wrote:

> Hello everybody, > > > > the last days I have experimented a bit with operator overloading in > userspace classes (redefing the meaning of arithmetic operations like +, -, > *, etc. for your own classes). > > This could be useful for different libraries which implements custom > arithmetic objects (like money values, tensors, etc.) or things like > Symfony > string component (concatenate) operator, because it improves readability > much: > > $x * ($a + $b) instead of $x->multiply($a->add($b)) > > > > 4 years ago, there was a RFC about this topic ( > <https://wiki.php.net/rfc/operator-overloading> > https://wiki.php.net/rfc/operator-overloading), which was discussed a bit > ( > <https://externals.io/message/89967> https://externals.io/message/89967), > but there was no real Outcome. > > > > I have tried to implement a proof of concept of the RFC, I encountered some > problems, when implementing the operator functions as (non-static) class > members and pass them only the “other” argument: What happens when we > encounter an expression like 2/$a and how can the class differ this from > $a/2. Also not every operation on every structure is e.g on commutative > (e.g. for matrices A*B =/= B*A). So I tried a C#-like approach, where the > operator implementations are static functions in the class, and both > arguments are passed. In my PHP implementation this would look something > like this: > > > > Class X { > > public static function __add($lhs, $rhs) { > > //... > > } > > } > > > > The class function can so decide what to do, based on both operands (so it > can decide if the developer wrote 2/$a or $a/2). Also that way an > implementor can not return $this by accident, which could lead to > unintended > side effect, if the result of the operation is somehow mutated. >
Using static methods sounds reasonable to me. I have taken over the idea of defining a magic function for each operation
> (like Python does), because I think that way it is the clearest way to see, > what operators a class implements (could be useful for static analysis). > The > downside to this approach is that this increases the number of magic > functions highly (my PoC-code defines 13 additional magic functions, and > the > unary operators are missing yet), so some people in the original discussion > suggest to define a single (magic) function, where the operator is passed, > and the user code decides, what to do. Advantageous is very extensible > (with > the right parser implementation, you could even define your own new > operators), with the cost that this method will become very complex for > data > structures which use multiple operators (large if-else or switch > constructions, which delegate the logic to the appropriate functions). An > other idea mentioned was to extract interfaces with common functionality > (like Arithmetically, Comparable, etc.) like done with the ArrayAccess or > Countable interfaces. The problem that I see here, is that this approach is > rather unflexible and it would be difficult to extract really universal > interfaces (e.g. vectors does not need a division (/) operation, but the > concatenation . could be really useful for implementing dot product). This > would lead to either that only parts of the interfaces are implemented (and > the other just throw exceptions) or that the interfaces contain only one or > two functions (so we would have many interfaces instead of magic functions > in the end). >
Yes, i don't think it makes sense to group these operations in interfaces, the use-cases are just too diverse. It's possible to define one interface per operator (e.g. what Rust does), though I don't think this is going to be particularly useful in PHP. I would not want to see functions accepting int|float|(Add&Mul) show up, because someone is trying to be overly generic in their interfaces ;) As to whether it should be a single method or multiple, I would go for multiple methods, as that makes it more clear which operators are overloaded from an API perspective. On the topic which operators should be overloadable: My PoC-implementation
> has magic functions for the arithmetic operators (+, -, *, /, %, **), > string > concatenation (.), and bit operations (>>, <<, &, |, ^). Comparison and > equality checks are implement using a common __compare() function, which > acts like an overload of the spaceship operator. Based if -1, 0 or +1 is > returned by the comparison operators (<, >, <=, >=, ==) are evaluated. I > think this way we can enforce, that the assumed standard logic (e.g > !($a<$b)=($a>=$b) and ($a<$b)=($b>$a)) of comparison is implemented.. Also I > don’t think this would restrict real world applications much (if you have > an > example, where a separate definition of < and >= could be useful, please > comment it). >
I would recommend not handling overloading of comparisons in the same proposal. Comparison is more widely useful than other overloading and has a more complex design space (especially when it comes to accommodating objects that can only be compared for equality/inequality for example). Comparison may also benefit more from having an interface than the other operators.
> Unlike the original idea, I don’t think it should be possible to overwrite > identity operator (===), because it should always be possible to check if > two objects are really identical (also every case should be coverable by > equality). The same applies to the logic operators (!, ||, &&), I think > they > should always work like intended (other languages like Python and C# > handles > it that way too). >
I agree that === should not be overloadable. || and && are short-circuiting, so overloading them in any meaningful way would be pretty hard anyway (we'd have to implicitly wrap the RHS into a closure or ... something?)
> For the shorthand assignment operators like +=, -= the situation is a bit > more complicated: On the one hand the user has learned that $a+=1 is just > an > abbreviation of $=$a+1, so this logic should apply to overloaded operators > as well (in C# it is implemented like this). On the other hand it could be > useful to differentiate between the two cases, so you can mutate the object > itself (in the += case) instead of returning a new object instance (the > class cannot know it is assigned to its own reference, when $a + 1 is > called). Personally I don’t think that this would be a big problem, so my > PoC-Code does not provide a possibility to override the short hand > operators.) For the increment/decrement operators ($a++) it is similar, it > would be nice if it would be possible to overload this operator but on the > other hand the use cases of this operator is really limited besides integer > incrementation and if you want to trigger something more complex, you > should > call a method, to make clear of your intent. >
At least to start with, I don't think we should offer separate overloading of $a += 1 and treat it as $a = $a +1, as the existing operator overloading implementation does. Operators currently only work on values that use by-value passing semantics, so if you write something like $b = $a = 1; $a += 1; then $a will be 2, but $b will be 1. Using the $a = $a + 1 expansion for operator overloading ensures that this is also the case for objects. Of course there are performance concerns here, and it could in some cases be significantly more efficient to perform an in-place modification. It is possible to allow that while still keeping the above semantics by only allowing an in-place modification if $a has no over users (something that we can check in the VM). But I don't think this should be part of an initial proposal.
> On the topic in which order the operators should be executed: Besides the > normal priority (defined by PHP), my code checks if the element on the left > side is an object and tries to call the appropriate magic function on it. > If > this is not possible the same is done for the right argument. This should > cover the most of the use cases, except some cases: Consider a expression > like $a / $b, where $a and $b has different classes (class A + class B). If > class B knows how to divide class A, but class A does not know about class > B, we encounter a problem when evaluating just from left to right (and > check > if the magic method exists). A solution for that would be that object $a > can > express that he does not know how to handle class B (e.g. by returning > null, > or throwing a special exception) and PHP can call the handler on object $b. > I'm not sure how common this problem would be, so I don’t have an idea how > useful this feature would be. >
That sounds reasonable to me.
> My proof-of-concept implementation can be found here: > <https://github.com/jbtronics/php-src> > https://github.com/jbtronics/php-src
Unfortunately, this implementation goes in the wrong direction: PHP already has full internal support for operator overloading through the do_operation object handler. Operator overloading should be exposed to userland through that handler as well.
> Here you can find some basic demo code using it: > <https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6> > https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6 > > > > I would be happy to hear some opinions for this concept, and the idea of > overloadable operators in PHP in general. >
Thanks for working on this :) I think overloaded operators are a reasonable addition to the language at this point. I think the main concern people tend to have in this area is that operator overloading is going to be abused (see for example << in C++). There are many very good use-cases for operator overloading though (as mentioned, vector/matrix calculations, complex, rationals, money, ...) Some of those are not common in PHP, but maybe the lack of operator overloading is part of the problem there ;) Regards, Nikita
  108318
January 29, 2020 12:12 oludonsexy@gmail.com (Olumide Samson)
Would there be an RFC to push this feature(with the right handler, POC)
into PHP?

Or something would stop it from happening?



On Wed, 29 Jan 2020, 10:20 am Nikita Popov, ppv@gmail.com> wrote:

> On Wed, Jan 29, 2020 at 12:14 AM boehmer@gmx.de> wrote: > > > Hello everybody, > > > > > > > > the last days I have experimented a bit with operator overloading in > > userspace classes (redefing the meaning of arithmetic operations like +, > -, > > *, etc. for your own classes). > > > > This could be useful for different libraries which implements custom > > arithmetic objects (like money values, tensors, etc.) or things like > > Symfony > > string component (concatenate) operator, because it improves readability > > much: > > > > $x * ($a + $b) instead of $x->multiply($a->add($b)) > > > > > > > > 4 years ago, there was a RFC about this topic ( > > <https://wiki.php.net/rfc/operator-overloading> > > https://wiki.php.net/rfc/operator-overloading), which was discussed a > bit > > ( > > <https://externals.io/message/89967> https://externals.io/message/89967 > ), > > but there was no real Outcome. > > > > > > > > I have tried to implement a proof of concept of the RFC, I encountered > some > > problems, when implementing the operator functions as (non-static) class > > members and pass them only the “other” argument: What happens when we > > encounter an expression like 2/$a and how can the class differ this from > > $a/2. Also not every operation on every structure is e.g on commutative > > (e.g. for matrices A*B =/= B*A). So I tried a C#-like approach, where the > > operator implementations are static functions in the class, and both > > arguments are passed. In my PHP implementation this would look something > > like this: > > > > > > > > Class X { > > > > public static function __add($lhs, $rhs) { > > > > //... > > > > } > > > > } > > > > > > > > The class function can so decide what to do, based on both operands (so > it > > can decide if the developer wrote 2/$a or $a/2). Also that way an > > implementor can not return $this by accident, which could lead to > > unintended > > side effect, if the result of the operation is somehow mutated. > > > > Using static methods sounds reasonable to me. > > I have taken over the idea of defining a magic function for each operation > > (like Python does), because I think that way it is the clearest way to > see, > > what operators a class implements (could be useful for static analysis).. > > The > > downside to this approach is that this increases the number of magic > > functions highly (my PoC-code defines 13 additional magic functions, and > > the > > unary operators are missing yet), so some people in the original > discussion > > suggest to define a single (magic) function, where the operator is > passed, > > and the user code decides, what to do. Advantageous is very extensible > > (with > > the right parser implementation, you could even define your own new > > operators), with the cost that this method will become very complex for > > data > > structures which use multiple operators (large if-else or switch > > constructions, which delegate the logic to the appropriate functions). An > > other idea mentioned was to extract interfaces with common functionality > > (like Arithmetically, Comparable, etc.) like done with the ArrayAccess or > > Countable interfaces. The problem that I see here, is that this approach > is > > rather unflexible and it would be difficult to extract really universal > > interfaces (e.g. vectors does not need a division (/) operation, but the > > concatenation . could be really useful for implementing dot product). > This > > would lead to either that only parts of the interfaces are implemented > (and > > the other just throw exceptions) or that the interfaces contain only one > or > > two functions (so we would have many interfaces instead of magic > functions > > in the end). > > > > Yes, i don't think it makes sense to group these operations in interfaces, > the use-cases are just too diverse. It's possible to define one interface > per operator (e.g. what Rust does), though I don't think this is going to > be particularly useful in PHP. I would not want to see functions accepting > int|float|(Add&Mul) show up, because someone is trying to be overly generic > in their interfaces ;) > > As to whether it should be a single method or multiple, I would go for > multiple methods, as that makes it more clear which operators are > overloaded from an API perspective. > > On the topic which operators should be overloadable: My PoC-implementation > > has magic functions for the arithmetic operators (+, -, *, /, %, **), > > string > > concatenation (.), and bit operations (>>, <<, &, |, ^). Comparison and > > equality checks are implement using a common __compare() function, which > > acts like an overload of the spaceship operator. Based if -1, 0 or +1 is > > returned by the comparison operators (<, >, <=, >=, ==) are evaluated. I > > think this way we can enforce, that the assumed standard logic (e.g > > !($a<$b)=($a>=$b) and ($a<$b)=($b>$a)) of comparison is implemented. > Also I > > don’t think this would restrict real world applications much (if you have > > an > > example, where a separate definition of < and >= could be useful, please > > comment it). > > > > I would recommend not handling overloading of comparisons in the same > proposal. Comparison is more widely useful than other overloading and has a > more complex design space (especially when it comes to accommodating > objects that can only be compared for equality/inequality for example). > Comparison may also benefit more from having an interface than the other > operators. > > > > Unlike the original idea, I don’t think it should be possible to > overwrite > > identity operator (===), because it should always be possible to check if > > two objects are really identical (also every case should be coverable by > > equality). The same applies to the logic operators (!, ||, &&), I think > > they > > should always work like intended (other languages like Python and C# > > handles > > it that way too). > > > > I agree that === should not be overloadable. || and && are > short-circuiting, so overloading them in any meaningful way would be pretty > hard anyway (we'd have to implicitly wrap the RHS into a closure or ... > something?) > > > > For the shorthand assignment operators like +=, -= the situation is a bit > > more complicated: On the one hand the user has learned that $a+=1 is just > > an > > abbreviation of $=$a+1, so this logic should apply to overloaded > operators > > as well (in C# it is implemented like this). On the other hand it could > be > > useful to differentiate between the two cases, so you can mutate the > object > > itself (in the += case) instead of returning a new object instance (the > > class cannot know it is assigned to its own reference, when $a + 1 is > > called). Personally I don’t think that this would be a big problem, so my > > PoC-Code does not provide a possibility to override the short hand > > operators.) For the increment/decrement operators ($a++) it is similar, > it > > would be nice if it would be possible to overload this operator but on > the > > other hand the use cases of this operator is really limited besides > integer > > incrementation and if you want to trigger something more complex, you > > should > > call a method, to make clear of your intent. > > > > At least to start with, I don't think we should offer separate overloading > of $a += 1 and treat it as $a = $a +1, as the existing operator overloading > implementation does. Operators currently only work on values that use > by-value passing semantics, so if you write something like > > $b = $a = 1; > $a += 1; > > then $a will be 2, but $b will be 1. Using the $a = $a + 1 expansion for > operator overloading ensures that this is also the case for objects. > > Of course there are performance concerns here, and it could in some cases > be significantly more efficient to perform an in-place modification. It is > possible to allow that while still keeping the above semantics by only > allowing an in-place modification if $a has no over users (something that > we can check in the VM). But I don't think this should be part of an > initial proposal. > > > > On the topic in which order the operators should be executed: Besides > the > > normal priority (defined by PHP), my code checks if the element on the > left > > side is an object and tries to call the appropriate magic function on it. > > If > > this is not possible the same is done for the right argument. This should > > cover the most of the use cases, except some cases: Consider a expression > > like $a / $b, where $a and $b has different classes (class A + class B).. > If > > class B knows how to divide class A, but class A does not know about > class > > B, we encounter a problem when evaluating just from left to right (and > > check > > if the magic method exists). A solution for that would be that object $a > > can > > express that he does not know how to handle class B (e.g. by returning > > null, > > or throwing a special exception) and PHP can call the handler on object > $b. > > I'm not sure how common this problem would be, so I don’t have an idea > how > > useful this feature would be. > > > > That sounds reasonable to me. > > > > My proof-of-concept implementation can be found here: > > <https://github.com/jbtronics/php-src> > > https://github.com/jbtronics/php-src > > > Unfortunately, this implementation goes in the wrong direction: PHP already > has full internal support for operator overloading through the do_operation > object handler. Operator overloading should be exposed to userland through > that handler as well. > > > > Here you can find some basic demo code using it: > > <https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6> > > https://gist.github.com/jbtronics/ee6431e52c161ddd006f8bb7e4f5bcd6 > > > > > > > > I would be happy to hear some opinions for this concept, and the idea of > > overloadable operators in PHP in general. > > > > Thanks for working on this :) I think overloaded operators are a reasonable > addition to the language at this point. I think the main concern people > tend to have in this area is that operator overloading is going to be > abused (see for example << in C++). There are many very good use-cases for > operator overloading though (as mentioned, vector/matrix calculations, > complex, rationals, money, ...) Some of those are not common in PHP, but > maybe the lack of operator overloading is part of the problem there ;) > > Regards, > Nikita >
  108319
January 29, 2020 14:39 jan.h.boehmer@gmx.de
> I would recommend not handling overloading of comparisons in the same proposal. Comparison is more widely useful than other overloading and has a more complex design space (especially when it comes to accommodating objects that can only be compared for equality/inequality for example). Comparison may also benefit more from having an interface than the other operators.
I understand your point. There was already an RFC with an similar idea (https://wiki.php.net/rfc/comparable). I think the idea of having an comparable interface could be really useful for things like sorting algorithms (so these could sort any comparable object). The case that structures must not define an order (but can have equality) is a good point, maybe this could be solved with two interfaces Comparable (which defines the spaceship operator) and another one like Matchable or Equalable (which only defines an is_equal function). I would not split the comparison operators any further (the RFC mentioned above even suggested to overload the not equal operator) or we end up with situations, where $a!=$b is not the same as !($a==$b), which would make the code using it very difficult to understand. It is maybe reasonable to split operator and comparison overloading into different RFCs so they can be discussed separately. But if PHP decides to offer operation overloading it should also offer a possibility to compare custom objects, or the operation overloading looses some of its intended convenience (like the situation in PHP 7.0 where you could define scalar type hints, but could not allow passing null easily).
> Of course there are performance concerns here, and it could in some cases be significantly more efficient to perform an in-place modification. It is possible to allow that while still keeping the above semantics by only allowing an in-place modification if $a has no over users (something that we can check in the VM). But I don't think this should be part of an initial proposal.
I agree. If there is real need for this case, it could be implemented later.
> Unfortunately, this implementation goes in the wrong direction: PHP already has full internal support for operator overloading through the do_operation object handler. Operator overloading should be exposed to userland through that handler as well.
I have seen this mechanism too late, and I have to understand a bit more how it works exactly, but I agree that this internal operator overloading mechanism should be used. I think it should be the goal that internal and userspace classes should appear the same to the user in points of the operator overloading, so an user can just call for example parent::__add() if he is extending a PHP class (e.g. Carbon does that for the datetime class). I will try to build an implementation using the do_operation handler, when I have time.
> Thanks for working on this :) I think overloaded operators are a reasonable addition to the language at this point. I think the main concern people tend to have in this area is that operator overloading is going to be abused (see for example << in C++). There are many very good use-cases for operator overloading though (as mentioned, vector/matrix calculations, complex, rationals, money, ...) Some of those are not common in PHP, but maybe the lack of operator overloading is part of the problem there ;)
Ultimately we cannot really control how people will end up using the operation overloading, but in my opinion the benefits outweighs the drawbacks (also hopefully nobody would be insane enough to replace the print function with << ;) ). With FFI-bindings it would be possible to build cool math/calculation libraries (similar to numpy in python), that can be accessed easily via PHP and does big calculations in native speed. I don’t think PHP will become an scientific used language like python, but sometimes some higher math function in PHP could be helpful. Also at least the handling of money values is quite common in web applications.
  108333
January 30, 2020 21:22 jan.h.boehmer@gmx.de
> Unfortunately, this implementation goes in the wrong direction: PHP already has full internal support for operator overloading through the do_operation object handler. Operator overloading should be exposed to userland through that handler as well.
I have made another implementation (https://github.com/jbtronics/php-src/tree/operator_overloading) which provides an standard handler for do_operation, which calls the functions in user space. I also removed the __compare magic function from my implementation, so this can be handled separately. Another thing: What are your opinions on overload the bitwise not operator (~)? My implementations offers every other bitwise operator and this one would complete the set of bitwise operators. On the other hand unary operators does not have much use for objects and maybe encourage people using them as "shortcut" for calling things on the object, where an explicit method call would make the intend function more clear. Regards, Jan
  108334
January 31, 2020 15:41 larry@garfieldtech.com ("Larry Garfield")
On Thu, Jan 30, 2020, at 3:22 PM, jan.h.boehmer@gmx.de wrote:
> > Unfortunately, this implementation goes in the wrong direction: PHP already has full internal support for operator overloading through the do_operation object handler. Operator overloading should be exposed to userland through that handler as well. > > I have made another implementation > (https://github.com/jbtronics/php-src/tree/operator_overloading) which > provides an standard handler for do_operation, which calls the > functions in user space. > > I also removed the __compare magic function from my implementation, so > this can be handled separately.
I cannot speak to the implementation details. From a design perspective, I am tentatively positive on operator overloading, with separate method for each operator, BUT, the big question for me is the rules around type compatibility. Can you only compare 2 of the same type? What about subclasses? Can that differ if a subclass overrides that method? What happens to commutativity then? Can you compare based on an interface? Examples: class Money { public function __add(Money $m) { ... } } class Dollar extends Money { public function__add(Money $m) { ... } } class Euro extends Money { } $m = new Money(5); $d = new Dollar(10); $e = new Euro(15); What should happen in each of these cases? $m + $d; $d + $m; $m + $e; $e + $m; $d + $e; Or similarly, is this allowed: interface Money { public function __add(Money $m); } There's a lot of very tricksy logic here to work out in terms of what makes sense to do before we could consider it. That logic may already have been figured out by another language (Python, C#, etc.) in which case I'm perfectly happy to steal their logic outright if it makes sense to do so. It's worth investigating before we go further to see what the traps are going to be. Also, I want to reiterate: Any of these operations MUST be designed to return a new value, never modify in place. These operators only make sense on value objects, not service objects, and value objects should be immutable. Which means that there is no __addEquals() method, just _add(), and we continue with that being isomorphic to $a = $a + $b;, the behavior of which is readily predictable. I've actually been wondering lately if we shouldn't create an entirely separate data structure for value objects that helps enforce that difference of expectation. (Similar to shapes in Hack, or records or structs in various other languages.)
> Another thing: What are your opinions on overload the bitwise not > operator (~)? My implementations offers every other bitwise operator > and this one would complete the set of bitwise operators. On the other > hand unary operators does not have much use for objects and maybe > encourage people using them as "shortcut" for calling things on the > object, where an explicit method call would make the intend function > more clear.
Frankly I'd avoid bitwise operators entirely for now. I'm not even sure how you'd use those... --Larry Garfield
  108335
January 31, 2020 15:55 ben@benramsey.com (Ben Ramsey)
> Also, I want to reiterate: Any of these operations MUST be designed to return a new value, never modify in place. These operators only make sense on value objects, not service objects, and value objects should be immutable.
I completely agree. This was the gist of my earlier comments. Maybe we should resurrect discussion of the immutable classes and properties RFC: https://wiki.php.net/rfc/immutability If we add the ability to specify immutability, then we can enforce in the engine that the left and right operands must be immutable. For example: public function __add(immutable $left, immutable $right); Cheers, Ben
  108419
February 6, 2020 20:29 chasepeeler@gmail.com (Chase Peeler)
On Fri, Jan 31, 2020 at 10:55 AM Ben Ramsey <ben@benramsey.com> wrote:

> > Also, I want to reiterate: Any of these operations MUST be designed to > return a new value, never modify in place. These operators only make sense > on value objects, not service objects, and value objects should be > immutable. > > I completely agree. This was the gist of my earlier comments. > > Maybe we should resurrect discussion of the immutable classes and > properties RFC: https://wiki.php.net/rfc/immutability > > If we add the ability to specify immutability, then we can enforce in the > engine that the left and right operands must be immutable. > > For example: > > public function __add(immutable $left, immutable $right); > > Cheers, > Ben > > Ideally, I don't think the items have to be immutable. Here is a silly
use-case: public function __add($left,$right){ $left->operatedOn++; $right->operatedOn++; return $left->value + $right->value; } However, given the nature of operator overloading, I think the users should EXPECT what they pass in will not be changed, unless they explicitly pass by reference. This means we'd have to "change the rules" for operator overloading magic methods, where objects are passed by value unless explicitly passed by reference ( public function __add(&$left, &$right) ). I think that is an even worse idea! So, I think you really have two options. Change the rules so that even objects are passed by value in this specific circumstance, and there is no ability to pass by reference. I still don't like this, because it changes the rules for only a specific scenario, but I think it's a better option than the one above, as well as a better option than allowing mutable objects 100% of the time - although, I'm not totally against spelling out in the documentation "Don't modify the items passed in or you'll get unexpected results!" The other options is the immutability RFC. This doesn't change the rules - it just adds a new rule. I don't see in that RFC, though, anything about the immutable type hints. That's really the only thing that I think is applicable to operator overloading. -- Chase Peeler chasepeeler@gmail.com
  108336
January 31, 2020 16:35 benjamin.morel@gmail.com (Benjamin Morel)
I like this whole operator overloading thing. I would probably use it in
brick/math <https://github.com/brick/math> and brick/money
<https://github.com/brick/money> to replace verbose `->plus()`,
`->multipliedBy()` etc. calls.

> Can you only compare 2 of the same type? What about subclasses? Can that differ if a subclass overrides that method? What happens to
commutativity then? Can you compare based on an interface? I think that this should behave exactly the same as if you replaced `$a + $b` with `$a->__add($b)` in your code. Nothing more, nothing less. The result will depend on whether you type-hinted your magic method or not.
> These operators only make sense on value objects, not service objects, and value objects should be immutable.
Indeed, we would need to make it clear that the operation must not modify the value of the current object, that should be effectively treated as immutable. Because it will probably be hard for the engine to enforce this, what about using the same convention I use in my libraries, i.e. `plus()` instead of `add()`, `dividedBy()` instead of `divide()`, etc.? This would translate to magic methods such as: __plus() __minus() __multipliedBy() __dividedBy() Benjamin
  108337
January 31, 2020 17:32 mike@newclarity.net (Mike Schinkel)
> On Jan 31, 2020, at 10:41 AM, Larry Garfield <larry@garfieldtech.com> wrote: > > I cannot speak to the implementation details. From a design perspective, I am tentatively positive on operator overloading, with separate method for each operator, BUT, the big question for me is the rules around type compatibility.
I have avoided commenting on this thread to see where it would lead. I have been surprised so many here are embracing operator overloading. My experience taught me operator overloading has been added to languages because "it seemed like a good idea at the time." But it is now considered to be harmful, by many: - https://www.quora.com/What-are-the-pitfalls-of-operator-overloading - https://cafe.elharo.com/programming/operator-overloading-considered-harmful/ - https://www.oreilly.com/library/view/sams-teach-yourself/0672324253/0672324253_ch21lev1sec4.html - https://en.wikipedia.org/wiki/Operator_overloading#Criticisms - https://www.jarchitect.com/QACenter/index.php?qa=53&qa_1=overload-operators-special-circumstances-defined-literals (Ruby's ability for developers to redefine the entire language is an especially chilling example of the concepts of operator overloading taken to the extreme: https://dev.to/jimsy/please-stop-using-ruby-4lf1) That said, I will not protest further if others still really feel strongly about adding operator overloading after reviewing those criticisms.
> Also, I want to reiterate: Any of these operations MUST be designed to return a new value, never modify in place. These operators only make sense on value objects, not service objects, and value objects should be immutable. > > Which means that there is no __addEquals() method, just _add(), and we continue with that being isomorphic to $a = $a + $b;, the behavior of which is readily predictable.
Immutability is a great feature from functional programming. But I think it is orthogonal to operator overloading as it would be (relatively) easy to implement an __add() method but how would PHP enforce that __add() would not be able to mutate state? Consider the following. How would __add() stop the mutating happening in $foo->bar->increment_ops()? class Bar { private $_op_count = 0; function increment_ops() { $this->_op_count++; } } class Foo { public $value; public $bar; function __construct( int $value ) { $this->value = $value; } function __add( Foo $foo ): Foo { $this->bar->increment_ops(); return new Foo( $this->value + $foo->value ); } } $foo = new Foo(10); $foo->bar = new Bar(); $foo = $foo + new Foo(5); echo $foo->value; I am sure it would be _possible_ to stop it, but I do not think it is trivial to architect nor implement. Given that I believe immutability would need to be implemented as an independent feature and not as an aspect of operator overloading. If we still want operator overloading and we want to force operator overloading to require immutability, I believe that means we would need an immutability RFC to be approved (and implemented?) before operator overloading requiring immutability could be approved. Something like this: class Bar { private $_op_count = 0; immutable function increment_ops() { global $foo; $foo = new Foo(); <====== Compile error! $this->_op_count++; <====== Compile error! } }
> I've actually been wondering lately if we shouldn't create an entirely separate data structure for value objects...
+1 for that. -Mike
  108338
January 31, 2020 18:35 ben@benramsey.com (Ben Ramsey)
> If we still want operator overloading and we want to force operator overloading to require immutability, I believe that means we would need an immutability RFC to be approved (and implemented?) before operator overloading requiring immutability could be approved. Something like this:
For reference, immutability has been proposed in the past, but I’m not sure where it landed. It looks like it fizzled out. * https://wiki.php.net/rfc/immutability * https://externals.io/message/94913 * https://externals.io/message/96919 * https://externals.io/message/97355 * https://externals.io/message/101890 * https://externals.io/message/81426 I agree with you that I think we need to accept an immutability RFC before operator overloading (requiring immutability) can be approved. I also believe operator overloading should require immutability. Cheers, Ben
  108340
February 1, 2020 00:03 larry@garfieldtech.com ("Larry Garfield")
On Fri, Jan 31, 2020, at 11:32 AM, Mike Schinkel wrote:
> > On Jan 31, 2020, at 10:41 AM, Larry Garfield <larry@garfieldtech.com> wrote: > > > > I cannot speak to the implementation details. From a design perspective, I am tentatively positive on operator overloading, with separate method for each operator, BUT, the big question for me is the rules around type compatibility. > > I have avoided commenting on this thread to see where it would lead. > I have been surprised so many here are embracing operator overloading. > > My experience taught me operator overloading has been added to > languages because "it seemed like a good idea at the time." But it is > now considered to be harmful, by many: > > - https://www.quora.com/What-are-the-pitfalls-of-operator-overloading > - > https://cafe.elharo.com/programming/operator-overloading-considered-harmful/ > - > https://www.oreilly.com/library/view/sams-teach-yourself/0672324253/0672324253_ch21lev1sec4.html > - https://en.wikipedia.org/wiki/Operator_overloading#Criticisms > - > https://www.jarchitect.com/QACenter/index.php?qa=53&qa_1=overload-operators-special-circumstances-defined-literals > > (Ruby's ability for developers to redefine the entire language is an > especially chilling example of the concepts of operator overloading > taken to the extreme: https://dev.to/jimsy/please-stop-using-ruby-4lf1) > > That said, I will not protest further if others still really feel > strongly about adding operator overloading after reviewing those > criticisms.
Those are valid points. (Hence my "tentatively.") Operator overloading is one of those features that when used well can be really really nice, but is really easy to use badly (in which case it's really really not nice). In all honesty, I'd probably be more excited about bringing back comparison overloading (__compare() and __equals()) than overriding arithmetic. Unless we could get some kind of bind operator, but that's probably asking for trouble. :-)
> > Also, I want to reiterate: Any of these operations MUST be designed to return a new value, never modify in place. These operators only make sense on value objects, not service objects, and value objects should be immutable. > > > > Which means that there is no __addEquals() method, just _add(), and we continue with that being isomorphic to $a = $a + $b;, the behavior of which is readily predictable. > > Immutability is a great feature from functional programming. But I > think it is orthogonal to operator overloading as it would be > (relatively) easy to implement an __add() method but how would PHP > enforce that __add() would not be able to mutate state?
Currently it cannot. That's another point of concern. We could at best document it and put "please please don't modify the object" in the docs, but that would probably work just as well as you think it would... All of this is pointing in the "we need a language construct for value objects" direction, which I believe would be highly useful but I don't know how we'd do it nicely. (Mainly, how to handle what PSR-7 does with withX() type methods, which are clunky but the best we can do without some really funky new syntax.) --Larry Garfield
  108341
February 1, 2020 09:22 nikita.ppv@gmail.com (Nikita Popov)
On Thu, Jan 30, 2020 at 10:22 PM boehmer@gmx.de> wrote:

> > Unfortunately, this implementation goes in the wrong direction: PHP > already has full internal support for operator overloading through the > do_operation object handler. Operator overloading should be exposed to > userland through that handler as well. > > I have made another implementation ( > https://github.com/jbtronics/php-src/tree/operator_overloading) which > provides an standard handler for do_operation, which calls the functions in > user space. >
Looks much better! If you submit a pull request, I can leave some more detailed comments.
> I also removed the __compare magic function from my implementation, so > this can be handled separately. > > Another thing: What are your opinions on overload the bitwise not operator > (~)? My implementations offers every other bitwise operator and this one > would complete the set of bitwise operators. On the other hand unary > operators does not have much use for objects and maybe encourage people > using them as "shortcut" for calling things on the object, where an > explicit method call would make the intend function more clear. >
Don't really see a reason not to support it. We do overload the bitwise not operator for GMP objects, and it would be equally applicable to other "integer" style objects. If you would like to start an RFC on this topic, please sign up for a wiki account (https://wiki.php.net/rfc/howto) and send me your username. Nikita
  108342
February 1, 2020 10:36 jan.h.boehmer@gmx.de
On Sat, Feb 1, 2020 10:22 AM ppv@gmail.com> wrote:
> Looks much better! If you submit a pull request, I can leave some more detailed comments.
Okay, I will submit a pull request with my changes.
> If you would like to start an RFC on this topic, please sign up for a wiki account (https://wiki.php.net/rfc/howto) and send me your username.
My Wiki account name is jbtronics. Thank you, Jan Böhmer
  108417
February 6, 2020 20:06 ajf@ajf.me (Andrea Faulds)
Hi,

Nikita Popov wrote:
> Yes, i don't think it makes sense to group these operations in interfaces, > the use-cases are just too diverse. It's possible to define one interface > per operator (e.g. what Rust does), though I don't think this is going to > be particularly useful in PHP. I would not want to see functions accepting > int|float|(Add&Mul) show up, because someone is trying to be overly generic > in their interfaces ;)
The use-cases being diverse can be an argument against being able to overload individual operators — if you consider using, for example, + to mean something other than addition to be an issue. I don't like what happens in languages like C++ where you do a bitwise left-shift to output to a stream or divide two strings to concatenate paths. Haskell has a nice approach (probably some other languages have this too) where it has typeclasses that contain related operators, which means that e.g. if you want to overload +, your type needs to support Num which also contains -, *, abs, the sign function, and conversion from integers. The obvious translation to PHP would be to use interfaces with related operations grouped. We could even support overloading some of the maths functions in theory (whether we should, I don't know…) Of course, some determined person will just implement * because it's cool and throw exceptions in the other methods… Also, Haskell also lets you just define arbitrary operators, which may even look the same as the built-in ones if you really want to, so maybe it's a bad comparison point :p Andrea