[RFC] Userspace operator overloading

php.internals

Unnamed Person

6 years ago
Hi internals, based on the discussions here (https://externals.io/message/108300) and here (https://github.com/php/php-src/pull/5156), I have created a proper RFC for userspace operator overloading: https://wiki.php.net/rfc/userspace_operator_overloading The main differences to my original concept, is the removed __compare() method (comparison overloading is a complex topic and should be handled in a different RFC) and the possibility to signal that the operator handler does not support the given types (by typehints or returning a special value). This way, only one of both objects has to know about the other type. This should expand the use case of operator overloading compared to my old concept. What do you think about the RFC? Some discussion points, I can think of, would be the naming of the methods (maybe naming them after the operator symbol and not the arithmetical operation they represent, e.g. __plus instead of __add) or putting the methods inside of interfaces like done for ArrayAccess (But I don’t see any advantage in doing so, as it is very difficult grouping different operators in a single interface usefully. Also, operators can accept and return different types, so there is no real common interface between classes you could rely on). Furthermore, maybe the idea of allowing operator overloading in general should be discussed as it is sometimes considered an anti-pattern (e.g. the usage of '<<' for outputting a string in C++). On the other hand there are many languages and libraries where operator overloading is used successfully (e.g. numpy in Python). Regards, Jan Böhmer

Mark Randall

6 years ago
On 15/02/2020 22:05, jan.h.boehmer@gmx.de wrote:
> Hi internals, > What do you think about the RFC?
"If an operator is used with an object that does not overload this operator, an NOTICE is triggered, which gives the user a hint about the method that has to be overloaded. For backward compatibility objects, which do not overload the operator, are converted to integer 1 (current behavior)." Noooo :( Notices are the enemy of all that is great and good. If operator overloading is going to become a first-class feature then it should be treated as such, and attempting overloading operators on objects that don't have the relevant method available should trigger an Error.
-- Mark Randall marandall@php.net

Rowan Collins

6 years ago
On 15 February 2020 22:05:52 GMT+00:00, jan.h.boehmer@gmx.de wrote:
>Some discussion points, I can think of, would be the naming of the >methods >(maybe naming them after the operator symbol and not the arithmetical >operation they represent, e.g. __plus instead of __add) or putting the >methods inside of interfaces like done for ArrayAccess
As I mentioned earlier [1] I think the answers to both of these questions depend on a fundamental philosophical question about what you're overloading: - The groups of operations which exist for built-in types e.g. the arithmetic operations overloaded by GMP; or string operations overloaded by an object with enhanced Unicode functionality. - The individual symbolic operators, with no intrinsic meaning - e.g. overloading . for concatenation on strings but dot-product for vectors; or a DSL overloading << and >> for "into" and "out of". I think it would benefit the RFC to take a stance on that question, and build the feature around it. [1] https://news-web.php.net/php.internals/108347
-- Rowan Tommins [IMSoP]

Stas Malyshev

6 years ago
Hi!
> - The individual symbolic operators, with no intrinsic meaning - e.g. > overloading . for concatenation on strings but dot-product for > vectors; or a DSL overloading << and >> for "into" and "out of".
Please no. I know it looks fancy and some languages love it, but for a person not in on the joke reading such code is a nightmare - it's basically gibberish until you learn all the types and which meaning each of the types assigns to operators. I mean, I could be maybe talked into having users overload "+" to mean "add some kind of objects that are like numbers but not actually integers" because my intuition still works there even if I don't know the exact type. But if someone thinks it's super-cool to override + to make it add element to the cache object - please no. It's way worse than just using plain old ->addObject(). You may save couple of keystrokes and make your code completely incomprehensible for people who come after you. I know DSLs have its niche but I seriously doubt average PHP user implements a lot of DSLs that are required to work natively on PHP engine.
-- Stas Malyshev smalyshev@gmail.com

Rowan Collins

6 years ago
On 16 February 2020 05:07:03 GMT+00:00, Stanislav Malyshev <smalyshev@gmail.com> wrote:
>Hi! > >> - The individual symbolic operators, with no intrinsic meaning - e.g. >> overloading . for concatenation on strings but dot-product for >> vectors; or a DSL overloading << and >> for "into" and "out of". > >Please no. I know it looks fancy and some languages love it, but for a >person not in on the joke reading such code is a nightmare
:) I know you're not alone in that feeling. If it turns out this is the majority view, I think it answers a couple of open questions: Overload methods should definitely be named after operations, not symbols, to remind people they are implementing addition, not giving new meaning to + They should probably be grouped into interfaces, which this RFC has so far resisted. How often does it make sense for a type to support addition but not subtraction, or multiplication but not division? Even more clearly, if a type claims to implement bitwise OR but not bitwise AND, NOT, and XOR, something is definitely fishy. You can't stop people using overloading DSL-style, but you can make it obvious that it's not the intention of the feature (if we agree that it's not the intention; maybe some people here are really hoping to use it that way?) Regards,
-- Rowan Tommins [IMSoP]

Girgias

6 years ago
On Sat, 15 Feb 2020 at 23:06, <jan.h.boehmer@gmx.de> wrote:
> Hi internals, > > based on the discussions here (https://externals.io/message/108300) and > here > (https://github.com/php/php-src/pull/5156), I have created a proper RFC > for > userspace operator overloading: > https://wiki.php.net/rfc/userspace_operator_overloading > > The main differences to my original concept, is the removed __compare() > method (comparison overloading is a complex topic and should be handled in > a > different RFC) and the possibility to signal that the operator handler does > not support the given types (by typehints or returning a special value). > This way, only one of both objects has to know about the other type. This > should expand the use case of operator overloading compared to my old > concept. > > What do you think about the RFC? > > Some discussion points, I can think of, would be the naming of the methods > (maybe naming them after the operator symbol and not the arithmetical > operation they represent, e.g. __plus instead of __add) or putting the > methods inside of interfaces like done for ArrayAccess (But I don’t see any > advantage in doing so, as it is very difficult grouping different operators > in a single interface usefully. Also, operators can accept and return > different types, so there is no real common interface between classes you > could rely on). > Furthermore, maybe the idea of allowing operator overloading in general > should be discussed as it is sometimes considered an anti-pattern (e.g. the > usage of '<<' for outputting a string in C++). On the other hand there are > many languages and libraries where operator overloading is used > successfully > (e.g. numpy in Python). > > Regards, > Jan Böhmer > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
Just as a FYI, I'll vote No to this RFC regardless of arguments as I find operator (and method) overloading a bad idea in general. Moreover, the only ones I am a tiny bit less reluctant to overload are the mathematical operators. However, one of the major reasons to want to have this seems to add better support for Matrices and Complex number which can already be done at the extension/engine level. Best regards George P. Banyard

Terje Slettebø

6 years ago
jan.h.boehmer@gmx.de wrote:
> based on the discussions here (https://externals.io/message/108300) > and here (https://github.com/php/php-src/pull/5156), I have created a > proper RFC for userspace operator overloading: > https://wiki.php.net/rfc/userspace_operator_overloading > > The main differences to my original concept, is the removed > __compare() method (comparison overloading is a complex topic and > should be handled in a different RFC) and the possibility to signal > that the operator handler does not support the given types (by > typehints or returning a special value). This way, only one of both > objects has to know about the other type. This should expand the use > case of operator overloading compared to my old concept. > > What do you think about the RFC?
I very much appreciate this initiative, as well as that of Patricio/Sara, and I agree with what people have said about the importance of realistic use-cases for something like this. Our company produces ads, and the price calculation is quite complex, involving the size of the ads, various discounts, etc. We're using objects for these calculations, to ensure we don't mix up units. Here are some examples: $price_per_column_mm = new PricePerColumnMm(1234); // Price = 1234 * number of columns * height in mm $height = new LengthInMm(100); $price_per_column = $price_per_column_mm->multiply($height); // Returns a PricePerColumn object $columns = new Column(4); $price = $price_per_column->multiply($columns); // Returns a Money object It gets more complicated, when we add in discounts: $discount_percentage = new Percentage(10); $end_user_price = $price->subtract($discount_percentage); // Price = Price * (100-discount percentage)/100 These are just a few examples, but it shows the advantage of using strongly typed objects in expressions, so that we don't do nonsensical things like multiplying two Money objects, or try to use a PricePerColumn object as a Money object. As you can probably tell, having all those "add", "subtract", "multiply", etc. method calls makes it hard to understand what is going on, and even if things happen in the right order, because when you use method calls, you can't change the order of execution without breaking an expression up into multiple expressions: It always happens from left to right. In order to support code like the above, we'd need to be able to define the following operators: PricePerColumnMm * LengthInMm -> PricePerColumn PricePerColumn * Column -> Money Money - Percentage -> Money This comes in addition to the usual operators that takes the same type on both sides, such as Money + Money, etc. Please note the last one, which is a non-commutative operation (Money - Percentage != Percentage - Money), which wouldn't work in Patricio's proposal, unless the "Associativity" section is rewritten to handle this in a different way. I like Patricio's proposal in that it uses ordinary, non-static methods, but unfortunately, it then runs into this problem, which seems to favour Jan's proposal. With regard to the mechanism for handling this (interfaces, magic methods, or a magic __overload() method), or Sara's proposal for registering overloads, using overload_type(), none of that are deal-breakers for me. Still, as we already have "magic methods" for other things (and interfaces, yes, I know), I'd lean towards implementing these as magic methods, which lets a developer only implement the operations that makes sense in their domain. Also, I think we should distinguish between non-assign and assign operators, giving them different method names, because some types may be expensive to copy, for example a large matrix, so $matrix *= $vector could be way more efficient than $matrix = $matrix * $vector. Regards, Terje