[VOTE] User Defined Operator Overloads

php.internals

Jordan LeDoux

4 years ago
Hello internals, I've opened voting on https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will close on 2022-01-17. To review past discussions on this RFC and the feature in general, please refer to: - https://externals.io/message/116611 | Current RFC discussion - https://externals.io/message/115764 | Initial RFC discussion - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding Jordan

Marco Pivetta

4 years ago
Hey Jordan, I've voted "no" on this one: infix functions may have been interesting, but adding a whole new type system around operators is really not worth it, given: * Added AST nodes * Added method definitions for niche use-cases * Complexity in support for static analysis tools I personally don't see a reason to introduce all this for examples like the one with GMP, which was more readable before adopting userland custom operators. In addition to all that, we didn't even achieve custom operators anyway: it's just the built-in ones (this is why I mentioned infix functions), and the precedence, number and type of operands are fixed too (yes, it is a sensible starting choice, but very little "custom" about it). Overall, your RFC is exactly what I would expect a custom operator RFC for PHP to look like: I just don't think the feature is needed at all, as it only makes the language much more complex, for rare cases that I hope I will never ever have to debug in future. Greets, Marco

Jordan LeDoux

4 years ago
On Sun, Jan 2, 2022 at 5:07 PM Marco Pivetta <ocramius@gmail.com> wrote:
> Hey Jordan, > > I've voted "no" on this one: infix functions may have been interesting, > but adding a whole new type system around operators is really not worth it, > given: > > * Added AST nodes > * Added method definitions for niche use-cases > * Complexity in support for static analysis tools > > I personally don't see a reason to introduce all this for examples like > the one with GMP, which was more readable before adopting userland custom > operators. > > In addition to all that, we didn't even achieve custom operators anyway: > it's just the built-in ones (this is why I mentioned infix functions), and > the precedence, number and type of operands are fixed too (yes, it is a > sensible starting choice, but very little "custom" about it). > > Overall, your RFC is exactly what I would expect a custom operator RFC for > PHP to look like: I just don't think the feature is needed at all, as it > only makes the language much more complex, for rare cases that I hope I > will never ever have to debug in future. > > Greets, > > Marco >
Thanks for articulating your reasons for your vote, I very much appreciate it. For the record, I don't think that my RFC precludes infix functions in a future RFC, and in fact I think setting up a new keyword makes that simpler in the future. Jordan

Andreas Hennings

4 years ago
On Mon, 3 Jan 2022 at 02:07, Marco Pivetta <ocramius@gmail.com> wrote:
> > Hey Jordan, > > I've voted "no" on this one: infix functions may have been interesting, but > adding a whole new type system around operators is really not worth it, > given: > > * Added AST nodes > * Added method definitions for niche use-cases > * Complexity in support for static analysis tools > > I personally don't see a reason to introduce all this for examples like the > one with GMP, which was more readable before adopting userland custom > operators. > > In addition to all that, we didn't even achieve custom operators anyway: > it's just the built-in ones (this is why I mentioned infix functions), and > the precedence, number and type of operands are fixed too (yes, it is a > sensible starting choice, but very little "custom" about it). > > Overall, your RFC is exactly what I would expect a custom operator RFC for > PHP to look like: I just don't think the feature is needed at all, as it > only makes the language much more complex, for rare cases that I hope I > will never ever have to debug in future.
Perhaps we need more discussion about use cases to justify this RFC? I think the main use cases mentioned so far were for arithmetic operations between value objects that represent money, time, or other measurable values, or mathematical higher-order values (vectors etc). I imagine that operator overloads can improve DX for these use cases, but at a cost for the overall language. How would you (Marco) see the future of arithmetic libraries for time, money etc without overloaded operators? How would these calculations look like e.g. with infix functions? Do you think this can eliminate the desire for operator overloads? E.g. something like this? $ts0 = new Timestamp($seconds0); $ts1 = new Timestamp($seconds1); /** @var Duration $duration */ $duration = $ts1 - $ts0; // Operator overload notation. $duration = Duration::betweenTimestamps($ts0, $ts1); // Static method notation. $duration = $ts1->diff($ts0); // Object method notation. $duration = $ts0 <Duration::betweenTimestamps> $ts1 // Infix notation based on static method. $duration = $ts1 <diff> $ts0 // Infix notation based on object method. I generally like names I can click on, if they don't get too long.. I like if the choice of implementation is knowable by just looking at the code. (with a small range of runtime polymorphism) I like if some functionality is implemented outside of object methods, keeping interfaces and inheritance chains simple. But for code with lots of calculations, a simple "-" feels simpler and more explanatory than something like "diff" or "timediff", where I have to reference and remember a specific method/function name. -- Andreas

Marco Pivetta

4 years ago
Hey Andreas, On Mon, Jan 3, 2022 at 3:40 PM Andreas Hennings <andreas@dqxtech.net> wrote:
> I imagine that operator overloads can improve DX for these use cases, > but at a cost for the overall language. > > How would you (Marco) see the future of arithmetic libraries for time, > money etc without overloaded operators? > How would these calculations look like e.g. with infix functions? > Do you think this can eliminate the desire for operator overloads? >
> E.g. something like this? > > $ts0 = new Timestamp($seconds0); > $ts1 = new Timestamp($seconds1); > /** @var Duration $duration */ > $duration = $ts1 - $ts0; // Operator overload notation. > $duration = Duration::betweenTimestamps($ts0, $ts1); // Static method > notation. > $duration = $ts1->diff($ts0); // Object method notation. > $duration = $ts0 <Duration::betweenTimestamps> $ts1 // Infix notation > based on static method. > $duration = $ts1 <diff> $ts0 // Infix notation based on object method. >
I'd probably use `$ts1->subtract($ts0)`, which doesn't seem to be that unreadable, and there is no need to abbreviate it to "diff" either. Whether it needs to be static methods or instance methods depends on the wished API design. What this RFC aims at is a mathematical language, inside another general purpose language: for complex expressions, I'd probably run it in a subsystem dedicated to this instead. See for example: * https://en.wikipedia.org/wiki/Expression_(mathematics) (I'm literally just picking a complex example - don't even know how to read that properly) * and its textual representation in https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)&action=edit&section=1 ```php $expression = <<<'MATH' f(a)+\sum_{k=1}^n\left.\frac{1}{k!}\frac{d^k}{dt^k}\right|_{t=0}f(u(t)) + \int_0^1 \frac{(1-t)^n }{n!} \frac{d^{n+1}}{dt^{n+1}} f(u(t))\, dt. MATH; $result = $userlandExpressionEngine->evaluate( $expression, [ // ... bind parameters here .... ] ); ``` Heck, I can probably ask the `$userlandExpressionEngine` to render that monstrosity for me (see attachment). Note that we do this stuff constantly for SQL, yet we haven't designed a system for embedding SQL into the language, and still, SQL is used many magnitudes more than all what was discussed in this RFC. Yes, strings are problematic to some degree, but it's still better than increasing language complexity for a very edge case. Alternatively, a better way to embed other languages can be used, which would be much more useful for things like Twig, Blade, PHPTal, SQL, etc: In haskell, this is done via Template Haskell, which many like, and many loathe, but is still useful for type-safe operations with a different language than the "main" one: https://wiki.haskell.org/A_practical_Template_Haskell_Tutorial#Shakespearean_Templates In addition to all the above, I just noticed that the entire reflection API in the RFC requires major BC breaks in the reflection API... sigh. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Pierre

4 years ago
Le 03/01/2022 à 16:00, Marco Pivetta a écrit :
> I'd probably use `$ts1->subtract($ts0)`, which doesn't seem to be that > unreadable, and there is no need to abbreviate it to "diff" either. > Whether it needs to be static methods or instance methods depends on > the wished API design. > > What this RFC aims at is a mathematical language, inside another > general purpose language: for complex expressions, I'd probably run it > in a subsystem dedicated to this instead. > > See for example: > >  * https://en.wikipedia.org/wiki/Expression_(mathematics) (I'm > literally just picking a complex example - don't even know how to read > that properly) >  * and its textual representation in > https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)&action=edit&section=1 > <https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)&action=edit&section=1> > > ```php > $expression = <<<'MATH' > f(a)+\sum_{k=1}^n\left.\frac{1}{k!}\frac{d^k}{dt^k}\right|_{t=0}f(u(t)) > + \int_0^1 \frac{(1-t)^n }{n!} \frac{d^{n+1}}{dt^{n+1}} f(u(t))\, dt. > MATH; > > $result = $userlandExpressionEngine->evaluate( >     $expression, >     [ >          // ... bind parameters here .... >     ] > ); > ``` > > Heck, I can probably ask the `$userlandExpressionEngine` to render > that monstrosity for me (see attachment). > > Note that we do this stuff constantly for SQL, yet we haven't designed > a system for embedding SQL into the language, and still, SQL is used > many magnitudes more than all what was discussed in this RFC. > > Yes, strings are problematic to some degree, but it's still better > than increasing language complexity for a very edge case. > > Alternatively, a better way to embed other languages can be used, > which would be much more useful for things like Twig, Blade, PHPTal, > SQL, etc: In haskell, this is done via Template Haskell, which many > like, and many loathe, but is still useful for type-safe operations > with a different language than the "main" one: > https://wiki.haskell.org/A_practical_Template_Haskell_Tutorial#Shakespearean_Templates > > In addition to all the above, I just noticed that the entire > reflection API in the RFC requires major BC breaks in the reflection > API... sigh. > > Marco Pivetta
Hello, I personally tend to agree with everything that Marco said. Especially regarding the fact that it's adding huge complexity to the language itself for mostly edge cases. I'd argue there's many much more valuable features that could be added to PHP before operator overload, such as generics, rationalized collection API and scalar objects with methods for example (which all could be magnificent tools for improving the operator overload RFC). I'm not against explicit method call, it's both readable and navigable, whereas operator overload tend to magically hide what the code really does. Best regards,
-- Pierre

Larry Garfield

4 years ago
On Mon, Jan 3, 2022, at 9:52 AM, Pierre wrote:
> I personally tend to agree with everything that Marco said. Especially > regarding the fact that it's adding huge complexity to the language > itself for mostly edge cases. > > I'd argue there's many much more valuable features that could be added > to PHP before operator overload, such as generics, rationalized > collection API and scalar objects with methods for example (which all > could be magnificent tools for improving the operator overload RFC).
Those are all independent of operator overloading. There's zero reason one needs to come before/after any other. Everyone wants generics, but they're really hard or Nikita would have implemented them already. This is a non-argument. Also, people keep talking about edge cases. In my experience, "are these two objects equal" (for some object-specific definition of equal) is very much *not* an edge case. I may have less use for overloading % as I don't use advanced math that much, but I compare things all the frickin' time and that would be incredibly useful day to day. --Larry Garfield

Pierre

4 years ago
Le 03/01/2022 à 17:12, Larry Garfield a écrit :
> On Mon, Jan 3, 2022, at 9:52 AM, Pierre wrote: > >> I personally tend to agree with everything that Marco said. Especially >> regarding the fact that it's adding huge complexity to the language >> itself for mostly edge cases. >> >> I'd argue there's many much more valuable features that could be added >> to PHP before operator overload, such as generics, rationalized >> collection API and scalar objects with methods for example (which all >> could be magnificent tools for improving the operator overload RFC). > Those are all independent of operator overloading. There's zero reason one needs to come before/after any other. Everyone wants generics, but they're really hard or Nikita would have implemented them already. This is a non-argument. > > Also, people keep talking about edge cases. In my experience, "are these two objects equal" (for some object-specific definition of equal) is very much *not* an edge case. I may have less use for overloading % as I don't use advanced math that much, but I compare things all the frickin' time and that would be incredibly useful day to day.
I think you read it too quickly. Anyway it was just examples, my point is not specifically about generics, but about the fact that operator overloading has been hugely controversial, and independently of the fact that I think this is a very well documented RFC, I still don't like it and express my opinion, I probably would not use those, but I eventually will be step debugging into some, and god, I don't wish I'll have to, really, I hate magic and non verbose code. Nevertheless, I have to admit, if I had the right to vote, I would abstain because it still a feature a lot of people want. Best regards,
-- Pierre

Pierre

4 years ago
Le 03/01/2022 à 17:12, Larry Garfield a écrit :
> Also, people keep talking about edge cases. In my experience, "are these two objects equal" (for some object-specific definition of equal) is very much *not* an edge case. I may have less use for overloading % as I don't use advanced math that much, but I compare things all the frickin' time and that would be incredibly useful day to day.
I forgot to answer to that, specifically. I'd much more prefer to have an explicit `equals(object $other): bool` (magic or not, really I do not care) single method for equality only, that'd be great. In that sense, I much preferred specific RFC about `__equalsTo()` or `__compareTo()` alone that a huge generic operator overload RFC. I think both could actually be separated, it wouldn't be that weird. Best regards,
-- Pierre

Jordan LeDoux

4 years ago
On Mon, Jan 3, 2022 at 9:07 AM Pierre <pierre-php@processus.org> wrote:
> I forgot to answer to that, specifically. I'd much more prefer to have > an explicit `equals(object $other): bool` (magic or not, really I do not > care) single method for equality only, that'd be great. In that sense, I > much preferred specific RFC about `__equalsTo()` or `__compareTo()` > alone that a huge generic operator overload RFC. I think both could > actually be separated, it wouldn't be that weird. >
Based on the feedback so far, I am... concerned that doing so would leave the mathematical improvements out in the cold. It seems that a non-trivial number of voters do not see mathematics as a desirable use case on its own. There's not really anything preventing that part from being ripped out on its own, but the mathematical overloads add almost no complexity on their own to the patch. One of my main goals in contributing to PHP is to improve its usability and performance in mathematics. Using magic methods instead of the operator syntax would take perhaps 2 hours to implement, it's a fairly trivial change in the scope of the RFC. However, I have no plans to bring this back in time for 8.2 should it be declined with a magic method implementation. Fundamentally, there are many voters that seem to be more concerned about possible abuse that empirically does not exist in most languages which have this feature, instead of improvements to one of the most neglected domains PHP is used in. And that's the real crux: *most* (but not all) of the objections raised so far suggest a future that factually does not exist in the example languages we can look at which already have this feature. C++ is perhaps the one real example of widespread abuse, however there are *multiple* aspects of this RFC which *specifically* target and discourage that kind of abuse. (Explicit typing of parameters, non-optional support for implied operators, restrictions on the return types of the == and <=> operators, errors and exceptions being propagated immediately, etc.) Further, the operand is not passed by-reference in this implementation, which flatly excludes many of the worst possible abuses. I understand all of these objections, but I do not agree with them. Obviously. If I did, then I wouldn't bring this RFC to a vote. What I have proposed is the most restricted version of operator overloads in any language I researched, and that is still not enough for many voters, some of whom have flatly stated that there is no version of this feature they would ever vote for. If that is the kind of headwind against quite fundamental improvements to mathematics within the language, then all of my energy will be required to produce any improvement at all, and I cannot spend effort on things which are unrelated. Jordan

Andreas Hennings

4 years ago
On Mon, 3 Jan 2022 at 22:15, Jordan LeDoux <jordan.ledoux@gmail.com> wrote:
> > On Mon, Jan 3, 2022 at 9:07 AM Pierre <pierre-php@processus.org> wrote: > > > I forgot to answer to that, specifically. I'd much more prefer to have > > an explicit `equals(object $other): bool` (magic or not, really I do not > > care) single method for equality only, that'd be great. In that sense, I > > much preferred specific RFC about `__equalsTo()` or `__compareTo()` > > alone that a huge generic operator overload RFC. I think both could > > actually be separated, it wouldn't be that weird. > > > > Based on the feedback so far, I am... concerned that doing so would leave > the mathematical improvements out in the cold. It seems that a non-trivial > number of voters do not see mathematics as a desirable use case on its own. > > There's not really anything preventing that part from being ripped out on > its own, but the mathematical overloads add almost no complexity on their > own to the patch. One of my main goals in contributing to PHP is to improve > its usability and performance in mathematics. > > Using magic methods instead of the operator syntax would take perhaps 2 > hours to implement, it's a fairly trivial change in the scope of the RFC. > However, I have no plans to bring this back in time for 8.2 should it be > declined with a magic method implementation. Fundamentally, there are many > voters that seem to be more concerned about possible abuse that empirically > does not exist in most languages which have this feature, instead of > improvements to one of the most neglected domains PHP is used in. > > And that's the real crux: *most* (but not all) of the objections raised so > far suggest a future that factually does not exist in the example languages > we can look at which already have this feature. C++ is perhaps the one real > example of widespread abuse, however there are *multiple* aspects of this > RFC which *specifically* target and discourage that kind of abuse. > (Explicit typing of parameters, non-optional support for implied operators, > restrictions on the return types of the == and <=> operators, errors and > exceptions being propagated immediately, etc.) > > Further, the operand is not passed by-reference in this implementation, > which flatly excludes many of the worst possible abuses. > > I understand all of these objections, but I do not agree with them. > Obviously. If I did, then I wouldn't bring this RFC to a vote. What I have > proposed is the most restricted version of operator overloads in any > language I researched, and that is still not enough for many voters, some > of whom have flatly stated that there is no version of this feature they > would ever vote for. If that is the kind of headwind against quite > fundamental improvements to mathematics within the language, then all of my > energy will be required to produce any improvement at all, and I cannot > spend effort on things which are unrelated.
To me it is not surprising that an RFC like this would be controversial. We could enter a wonderful new era of value objects with operators, but it is hard to envision that beforehand. The best might be to find and advertise with use cases and examples from other languages. (as mentioned, I don't really know where to start looking, otherwise I would have already shared something)

Jordan LeDoux

4 years ago
On Mon, Jan 3, 2022 at 4:05 PM Andreas Hennings <andreas@dqxtech.net> wrote:
> To me it is not surprising that an RFC like this would be controversial. > We could enter a wonderful new era of value objects with operators, > but it is hard to envision that beforehand. > > The best might be to find and advertise with use cases and examples > from other languages. > (as mentioned, I don't really know where to start looking, otherwise I > would have already shared something) >
Oh, this was always going to be a controversial RFC. :) Operator overloading is controversial in most languages, even the ones that have it and don't really have problems because of it. It's the kind of feature that doesn't affect most people most of the time, but that all developers end up having a strong opinion about one way or another. I talked about that a little bit in the internals podcast episode I did with Derick. NumPy is probably the absolute best example of this feature being used. NumPy is an absolute killer feature of Python, and drives an enormous amount of the development being done in the language. It could easily be pointed out that NumPy is the equivalent of an extension however, being compiled down and directly hooking into the Python language. NumPy is also not included in the core Python language, though operator overloading *is*, importantly. However, the toolchain for adding NumPy is much easier and consistent than the toolchain for PHP extensions in general, and NumPy didn't have to add overloads themselves as part of the extension (or teach Python devs the rules around using them in general). So as another example, I'll pull up the math libraries in PHP. I maintain a math library myself, but it isn't nearly as widely used as the works by Mark Baker. The mathematical uses have been described here as "niche" and "small". First let's see what Mark has to say about operator overloading (and this RFC specifically):
> Likewise, I have libraries for Matrix and Complex Numbers, where this
would be an incredibly useful addition.... a similar RFC using magic was proposed 6 years ago, and sadly never made it :-( I hope this time it is accepted, especially as a new keyword feels better than magic - Source: https://twitter.com/Mark_Baker/status/1477957056464359427 The libraries that Mark is referring to, markbaker/matrix and markbaker/complex, have over 105 million installs combined on packagist. (Source: https://packagist.org/?query=markbaker )
> And so would `$lineValue = $unitPrice * $quantity;` work fine, with less
words to read... and people who don't understand operator precedence would have problems with more complex mathematical formulae whether it was operator overloading or method name - Source: https://twitter.com/Mark_Baker/status/1478137583989309440
> But why shouldn't it be `==`... these are valid use cases for a
comparison; equally, vector additions ($vector1 + $vector2) are quite commonplace in mathematics - Source: https://twitter.com/Mark_Baker/status/1478131480383606787 Marco's response to the person who maintains the literal largest complex mathematics library in the entire language was:
> Yeah, so use matlab?
This is what I'm struggling with. I am seeing a lot of things here that sound an awful lot like "that's not *my* use case, so no". That isn't how you design the language. Nikita's objection to the syntax is much easier for me to understand, because it is based on *language design*. Mathematics isn't something that is never used or never needed in PHP. The idea that I need to justify this statement is somewhat alarming. I have provided not only a multitude of examples of use cases in the RFC, but I have repeatedly done so on this list as well. I have provided examples in mathematics and examples in other domains, such as with Collections, with userland scalar objects. I've mentioned possible future scope to expand further into things like better support for query builders. I do not believe the reason people vote no for this RFC is because they haven't been shown any use cases. The use cases are self-apparent and have *also* been provided, both within the current PHP ecosystem and outside of it. Both with existing extensions and userland libraries. I sincerely hope that the reason someone votes no on this RFC isn't because they think use cases don't exist or because the use cases are fringe. I also hope that it's not because of "abuse", as this is a boogeyman that is not supported by the ecosystems of nearly all languages with this feature. The problem is that without those two objections, I'm left with few reasons I could see to vote no, perhaps the most prominent being the one Nikita raised with finding the syntax objectionable. The syntax offers a lot for future scope in my opinion, some of it not even related to operator overloads. It provides us a better way forward for allowing objects to control casting, for instance. Many find the __toString() implementation to be lacking. Controlled casting in C++ for example however is accomplished with something like `operator (int): int`. Another suggestion that came through from the community after voting opened was to use something like `symmetric operator` to show that an operator can be in either position. It also could provide us with some way forward on improving/deprecating ArrayAccess. Burden on static analysis tools perhaps? But there are features that are difficult on static analysis that have been accepted. (Enums comes to mind.) If anyone is voting no truly because they are concerned that there aren't use cases to justify it, I'd like to hear that and what sort of examples they are looking for, because I feel I have done this multiple times on this list at this point. Jordan

Andreas Leathley

4 years ago
On 03.01.22 17:12, Larry Garfield wrote:
> Also, people keep talking about edge cases. In my experience, "are > these two objects equal" (for some object-specific definition of > equal) is very much *not* an edge case. I may have less use for > overloading % as I don't use advanced math that much, but I compare > things all the frickin' time and that would be incredibly useful day > to day.
Maybe some of the resistance against this RFC is the (understandable) focus on mathematical (or mathematical-adjacent) operations, making it seem that the "real" use-cases are narrow, whereas comparing objects could make quite a bit of code more readable, like comparing addresses, prices, availability or location/position. Having regular methods for operators is possible, but when used a lot the readability does start to suffer. The difficulty in arguing for operator overloading might be that the use cases are specific to a codebase and mainly become apparent once it is available (currently you just work around it). I do think it is a reasonable tool to make some code vastly more readable, and I don't see "novices" using operators a lot - PHP has many simpler features to abuse that can make code absolutely terrible. The RFC is really well thought out and reasoned, by the way - great work Jordan!

Andreas Hennings

4 years ago
On Mon, 3 Jan 2022 at 16:00, Marco Pivetta <ocramius@gmail.com> wrote:
> > Hey Andreas, > > On Mon, Jan 3, 2022 at 3:40 PM Andreas Hennings <andreas@dqxtech.net> wrote: >> >> I imagine that operator overloads can improve DX for these use cases, >> but at a cost for the overall language. >> >> How would you (Marco) see the future of arithmetic libraries for time, >> money etc without overloaded operators? >> How would these calculations look like e.g. with infix functions? >> Do you think this can eliminate the desire for operator overloads? >> >> >> E.g. something like this? >> >> $ts0 = new Timestamp($seconds0); >> $ts1 = new Timestamp($seconds1); >> /** @var Duration $duration */ >> $duration = $ts1 - $ts0; // Operator overload notation. >> $duration = Duration::betweenTimestamps($ts0, $ts1); // Static method notation. >> $duration = $ts1->diff($ts0); // Object method notation. >> $duration = $ts0 <Duration::betweenTimestamps> $ts1 // Infix notation >> based on static method. >> $duration = $ts1 <diff> $ts0 // Infix notation based on object method. > > > I'd probably use `$ts1->subtract($ts0)`, which doesn't seem to be that unreadable, and there is no need to abbreviate it to "diff" either. > Whether it needs to be static methods or instance methods depends on the wished API design.
Ok for "subtract", I should have thought of that :) Although for me "subtract" sounds like a possibly mutable method, whereas "diff" sounds immutable. This said, I am not really convinced that the current syntax is "good enough".
> > What this RFC aims at is a mathematical language, inside another general purpose language: for complex expressions, I'd probably run it in a subsystem dedicated to this instead.
For isolated "complex expressions", perhaps. But we should think about algorithms with multiple small math operations, e.g. a foreach with a repeated calculations, some of them in conditional branches, e.g. to calculate prices, taxes, rent etc. In all these operations, we want static analysis to check compatibility of operands. I think these algorithms will become more readable with overloaded operators, while any "embedded formula engine" would make them vastly more complex. Btw others already pointed out that we also want comparison of value objects, not just math. But I came up with the math use cases, so am sticking to it to be fair. Personally I still don't have a clear opinion for or against, but I also don't have voting rights, so...
> > See for example: > > * https://en.wikipedia.org/wiki/Expression_(mathematics) (I'm literally just picking a complex example - don't even know how to read that properly) > * and its textual representation in https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)&action=edit&section=1 > > ```php > $expression = <<<'MATH' > f(a)+\sum_{k=1}^n\left.\frac{1}{k!}\frac{d^k}{dt^k}\right|_{t=0}f(u(t)) + \int_0^1 \frac{(1-t)^n }{n!} \frac{d^{n+1}}{dt^{n+1}} f(u(t))\, dt. > MATH; > > $result = $userlandExpressionEngine->evaluate( > $expression, > [ > // ... bind parameters here .... > ] > ); > ``` > > Heck, I can probably ask the `$userlandExpressionEngine` to render that monstrosity for me (see attachment). > > Note that we do this stuff constantly for SQL, yet we haven't designed a system for embedding SQL into the language,
Actually we do have query builders, to reduce the amount of literal SQL strings in code, and to make it feel more "native". And where we do have literal SQL, we have to be very careful not to break things, especially when binding or escaping/encoding variables.
> and still, SQL is used many magnitudes more than all what was discussed in this RFC.
This may be true currently, for the arithmetics use cases. This said, the reason for a small amount of math libraries in PHP (which I did not actually count) could be exactly because of a lack of overloaded operators. And I think timestamp calculations are already relevant enough in today's code. It would be interesting to see how much overloaded operators are used in Python, and whether the good outweighs the bad. Unfortunately I don't really know where to start looking.
> > Yes, strings are problematic to some degree, but it's still better than increasing language complexity for a very edge case. > > Alternatively, a better way to embed other languages can be used, which would be much more useful for things like Twig, Blade, PHPTal, SQL, etc: In haskell, this is done via Template Haskell, which many like, and many loathe, but is still useful for type-safe operations with a different language than the "main" one: https://wiki.haskell.org/A_practical_Template_Haskell_Tutorial#Shakespearean_Templates > > In addition to all the above, I just noticed that the entire reflection API in the RFC requires major BC breaks in the reflection API... sigh.
Interesting. What exactly is going to break BC?

Bob Weinand

4 years ago
> Am 03.01.2022 um 01:13 schrieb Jordan LeDoux <jordan.ledoux@gmail.com>: > > Hello internals, > > I've opened voting on > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > close on 2022-01-17. > > To review past discussions on this RFC and the feature in general, please > refer to: > > - https://externals.io/message/116611 | Current RFC discussion > - https://externals.io/message/115764 | Initial RFC discussion > - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding > > Jordan
Hey Jordan, thanks for bringing it up to a vote. I've voted for an inclusion, for the primary reason, that in general, it does, in fact, not get abused too much. It seems to me, that many of the no-voters fear codebases riddled with random operator overloads where they make no sense. I don't share that sentiment. Yes, there will always be some outliers, but it shouldn't hinder the general improvement it brings to readability and expressiveness. (I strongly disagree that gmp overloads are a net negative.) For my part, I have had a positive experience with operator overloads in C#. They tend to not be overused, make Vector operations graspable (once you have a mental model of what vector operations feel and look like…). In the past I've really hated writing, and especially reading math heavy code on something different than the standard euclidean space in PHP. (Vectors, complex numbers, integer spaces with a finite size…) I truly hope this RFC passes, so that these abominations of nested math calls may disappear. Bob

Larry Garfield

4 years ago
On Sun, Jan 2, 2022, at 6:13 PM, Jordan LeDoux wrote:
> Hello internals, > > I've opened voting on > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > close on 2022-01-17. > > To review past discussions on this RFC and the feature in general, please > refer to: > > - https://externals.io/message/116611 | Current RFC discussion > - https://externals.io/message/115764 | Initial RFC discussion > - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding > > Jordan
I have voted Yes on this RFC as well. PHP right now is littered with inconsistencies between its various types. Redesigning the whole thing is obviously impossible, but allowing objects to hook into behaving like other values piecemeal (as this RFC does) is probably the best option available. Not every object will need every operator, and that's fine, but in places where they are useful, they will be *really* useful. I just ran into a place 2 weeks ago where the <=> or == overrides would have saved me a ton of time and effort, and allowed objects to work with more existing utilities. (In this case, in_array() with value objects, which right now is impossible without a ton more pointless work). Just those alone justify the RFC for me. I also believe this RFC handles more edge cases to nudge people toward good usage than any other proposal could. I really don't see how it could do better, without relying on other, even more controversial unimplemented features that may never exist (method pattern matching). I do think this is the best we'll be able to do on this front, it solves a real and present problem I do see in my own code, and it has clear extension points for the future (e.g., supporting arbitrary operator symbols if we ever decide to). That's all we can ask for from an RFC, so this has my vote. (I also find it odd to argue that it is bad because it will lead to confusing code, but at the same time argue it's bad because it doesn't allow arbitrary operator definitions. Those seem mutually contradictory positions.) --Larry Garfield

Andreas Hennings

4 years ago
One question that just occured to me: Why allow the "public" keyword for visibility, if operators are public automatically, and "private" and "protected" are not allowed? Do we plan for private/protected operators in the future? Shouldn't we eliminate meaningless choice? On Mon, 3 Jan 2022 at 01:14, Jordan LeDoux <jordan.ledoux@gmail.com> wrote:

Nikita Popov

4 years ago
On Mon, Jan 3, 2022 at 1:14 AM Jordan LeDoux <jordan.ledoux@gmail.com> wrote:
> Hello internals, > > I've opened voting on > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > close on 2022-01-17. > > To review past discussions on this RFC and the feature in general, please > refer to: > > - https://externals.io/message/116611 | Current RFC discussion > - https://externals.io/message/115764 | Initial RFC discussion > - https://externals.io/message/115648 | Pre-RFC discussion and > fact-finding > > Jordan >
Voted No on this one. I did support the previous proposal on this topic, but I don't like the changes that this iteration has introduced relative to the previous proposal. The part that I dislike most (and that I consider an exclusion criterion regardless of any other merits of the proposal) is the introduction of a new "operator +" style syntax. I found the motivation for this choice given in the RFC rather weak -- it seems to be a very speculative forward-compatibility argument, and I'm not sure it holds water even if we accept the premise. There's nothing preventing us, from a technical point-of-view, from allowing the use of some keyword only with magic methods. On the other hand, the cost of this move is immediate: All tooling will have to deal with a new, special kind of method declaration. I'm also not a fan of the OperandPosition approach, though I could probably live with it. The previous approach using static methods seemed more natural to me, especially when it comes to operators that do not typically commute (e.g. subtraction). Regards, Nikita

David Gebler

4 years ago
On Mon, Jan 3, 2022 at 5:38 PM Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Mon, Jan 3, 2022 at 1:14 AM Jordan LeDoux <jordan.ledoux@gmail.com> > wrote: > > > Hello internals, > > > > I've opened voting on > > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting > will > > close on 2022-01-17. > > > > To review past discussions on this RFC and the feature in general, please > > refer to: > > > > - https://externals.io/message/116611 | Current RFC discussion > > - https://externals.io/message/115764 | Initial RFC discussion > > - https://externals.io/message/115648 | Pre-RFC discussion and > > fact-finding > > > > Jordan > > > > Voted No on this one. I did support the previous proposal on this topic, > but I don't like the changes that this iteration has introduced relative to > the previous proposal. > > The part that I dislike most (and that I consider an exclusion criterion > regardless of any other merits of the proposal) is the introduction of a > new "operator +" style syntax. I found the motivation for this choice given > in the RFC rather weak -- it seems to be a very speculative > forward-compatibility argument, and I'm not sure it holds water even if we > accept the premise.
There's nothing preventing us, from a technical
> point-of-view, from allowing the use of some keyword only with magic > methods. On the other hand, the cost of this move is immediate: All tooling > will have to deal with a new, special kind of method declaration. >
I agree wholeheartedly with this. I don't have a vote but if I did I'd vote no. I think if operator overloading is to be introduced, new magic methods using names like __add and __equals are preferable for a number of reasons.

Pierre Joye

4 years ago
Hi Nikita, On Tue, Jan 4, 2022 at 12:38 AM Nikita Popov <nikita.ppv@gmail.com> wrote:
> > On Mon, Jan 3, 2022 at 1:14 AM Jordan LeDoux <jordan.ledoux@gmail.com> > wrote: > > > Hello internals, > > > > I've opened voting on > > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > > close on 2022-01-17. > > > > To review past discussions on this RFC and the feature in general, please > > refer to: > > > > - https://externals.io/message/116611 | Current RFC discussion > > - https://externals.io/message/115764 | Initial RFC discussion > > - https://externals.io/message/115648 | Pre-RFC discussion and > > fact-finding > > > > Jordan > > > > Voted No on this one. I did support the previous proposal on this topic, > but I don't like the changes that this iteration has introduced relative to > the previous proposal. > > The part that I dislike most (and that I consider an exclusion criterion > regardless of any other merits of the proposal) is the introduction of a > new "operator +" style syntax. I found the motivation for this choice given > in the RFC rather weak -- it seems to be a very speculative > forward-compatibility argument, and I'm not sure it holds water even if we > accept the premise. There's nothing preventing us, from a technical > point-of-view, from allowing the use of some keyword only with magic > methods. On the other hand, the cost of this move is immediate: All tooling > will have to deal with a new, special kind of method declaration. > > I'm also not a fan of the OperandPosition approach, though I could probably > live with it. The previous approach using static methods seemed more > natural to me, especially when it comes to operators that do not typically > commute (e.g. subtraction).
I hesitated too, however I think we can't escape this feature. Like it was for the annotation, we need to find a compromise. Your points are valid so I wonder if the RFC could be modified and get to the point we could reach that compromise. There will be the oppositions for the features as a whole, however I am optimistic about our abilities to get there this time rather than wait yet again a few years for something we know we will have anyway. Best.
-- Pierre @pierrejoye | http://www.libgd.org

Jordan LeDoux

4 years ago
On Tue, Jan 4, 2022 at 10:50 PM Pierre Joye <pierre.php@gmail.com> wrote:
> > I hesitated too, however I think we can't escape this feature. Like it > was for the annotation, we need to find a compromise. > > Your points are valid so I wonder if the RFC could be modified and get > to the point we could reach that compromise. There will be the > oppositions for the features as a whole, however I am optimistic about > our abilities to get there this time rather than wait yet again a few > years for something we know we will have anyway. >
I will certainly be making changes before bringing this RFC back if it is rejected. What exactly those changes are I am not certain yet, but the feedback I receive here from voters will obviously have a large impact on that. As i mentioned elsewhere, swapping to a magic method syntax would be about 2-3 hours of work, I could do that very quickly. But I don't want to make a change like that after the voting has started, so any such changes will have to wait. I suppose the alternative would be to withdraw the RFC now that a wider variety of voters are providing feedback than the other three threads. Jordan

Andreas Heigl

4 years ago
Hey Jordan. On 05.01.22 10:42, Jordan LeDoux wrote:
> On Tue, Jan 4, 2022 at 10:50 PM Pierre Joye <pierre.php@gmail.com> wrote: > >> >> I hesitated too, however I think we can't escape this feature. Like it >> was for the annotation, we need to find a compromise. >> >> Your points are valid so I wonder if the RFC could be modified and get >> to the point we could reach that compromise. There will be the >> oppositions for the features as a whole, however I am optimistic about >> our abilities to get there this time rather than wait yet again a few >> years for something we know we will have anyway. >> > > I will certainly be making changes before bringing this RFC back if it is > rejected. What exactly those changes are I am not certain yet, but the > feedback I receive here from voters will obviously have a large impact on > that. As i mentioned elsewhere, swapping to a magic method syntax would be > about 2-3 hours of work, I could do that very quickly. But I don't want to > make a change like that after the voting has started, so any such changes > will have to wait. I suppose the alternative would be to withdraw the RFC > now that a wider variety of voters are providing feedback than the other > three threads.
I'd rather not see more magic being introduced. That would for me be a reason to vote against the RFC. No matter what it does. Just my 0.02 € Cheers Andreas
-- ,,, (o o) +---------------------------------------------------------ooO-(_)-Ooo-+ | Andreas Heigl | | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | | https://andreas.heigl.org | +---------------------------------------------------------------------+ | https://hei.gl/appointmentwithandreas | +---------------------------------------------------------------------+

Andreas Leathley

4 years ago
On 05.01.22 10:42, Jordan LeDoux wrote:
> I suppose the alternative would be to withdraw the RFC > now that a wider variety of voters are providing feedback than the other > three threads.
I would let the vote go on, as there are still many voters who have not voted on this yet, giving the proposal more exposure and also giving you a more complete feedback about the outcome. And if this RFC has less voters in total when it ends than other RFCs, that could also be good to know. There have been quite a few big PHP features which failed in the first attempt, but then easily passed in a later attempt after more discussions and changes. Also, your hint about the operator keyword decision and on what that is based on might offer more avenues to change some minds - personally, I would be interested to read a blog article about this (even if it is quite lengthy, and even though I don't have a vote and am not designing languages), and linking that in an RFC could then be some helpful background for some people, even if not everyone reads it. And such an article might be interesting to people outside of PHP.

Andreas Hennings

4 years ago
Hello Jordan, I have another note about the RFC. (I am not sure what's the policy, if we should continue _all_ discussion here or go back to the RFC thread. Hope it's ok here.) OperandPosition::LeftSide OperandPosition::RightSide I wonder if this is the best way to model this. Especially, it does not account for the case where an operator only works in one direction, or the allowed operand type is dependent on the direction. E.g., (Money / float) is ok, but (float / Money) probably not supported. Or if it is supported, then the return type will be quite different. You can throw an exception, but this is not useful for static analysis. An alternative syntax with a few more keywords: abstract class Money { symmetric operator * (float|int $other): Money; // Commutative. left operator / (float|int $other): Money; // Only $a / $b allowed, $b / $a not possible. left operator - (Money $other): Money; // $a - $b right operator - (Money $other): Money; // $b - $a } Btw, in the Matrix example from the RFC, under "When will $operandPos be useful?", the $operandPos is not really useful, because it will always pick the default $a - $b version. The same applies to "-" operator in my Money example, because $other already implements the operator. The $operandPos is only needed if the left operand does _not_ implement the operator. Which is the case e.g. for complex numbers, or the Number class from the RFC example. I am trying to think of cases where ($a <op> $b) would have a different type than ($b <op> $a), but I can't think of any. Or rather, for any case that I could think of, the mirror operator could simply be provided by $b. I am not married to the modifier names, e.g. it could be "symmetric" or "commutative" or something else. For left/right perhaps I prefer to talk about the "default direction" vs the "flipped direction", not sure how this could be turned into keywords. If we don't like more keywords, perhaps something like "!operator" for the flipped version? Cheers Andreas On Mon, 3 Jan 2022 at 01:14, Jordan LeDoux <jordan.ledoux@gmail.com> wrote:

Andreas Hennings

4 years ago
Hello Jordan, do you have any thoughts about these symmetric/left/right modifiers, to get rid of the $operandPos parameter? To me, the parameter could be the kind of thing where in hindsight we ask "why?". Also, can we drop the "public" modifier, if we do a new version of the RFC? Cheers Andreas On Tue, 4 Jan 2022 at 00:27, Andreas Hennings <andreas@dqxtech.net> wrote:

Jordan LeDoux

4 years ago
On Wed, Jan 5, 2022 at 6:33 AM Andreas Hennings <andreas@dqxtech.net> wrote:
> Hello Jordan, > do you have any thoughts about these symmetric/left/right modifiers, > to get rid of the $operandPos parameter? > > To me, the parameter could be the kind of thing where in hindsight we > ask "why?". > > Also, can we drop the "public" modifier, if we do a new version of the RFC? > > Cheers > Andreas >
It's a totally different design concept (symmetric/left/right) and I've been going over the design implications before I responded. For instance, wouldn't this be a special case of method overloading? You're overloading according to a modifier, not the typing, but there is that to contend with. If someone defined `symmetric operator +` and `left operator +` which would be used? (My feeling is left as its more specific.) How would they be stored within the zend_class_entry? Since they would technically have the same name, something would need to happen for them to not be in the function table. The public modifier is not required (as stated in the RFC), you can just add it if you want. Are you asking for `public operator` to produce a compile error? Jordan

Andreas Hennings

4 years ago
On Wed, 5 Jan 2022 at 20:11, Jordan LeDoux <jordan.ledoux@gmail.com> wrote:
> > > > On Wed, Jan 5, 2022 at 6:33 AM Andreas Hennings <andreas@dqxtech.net> wrote: >> >> Hello Jordan, >> do you have any thoughts about these symmetric/left/right modifiers, >> to get rid of the $operandPos parameter? >> >> To me, the parameter could be the kind of thing where in hindsight we >> ask "why?". >> >> Also, can we drop the "public" modifier, if we do a new version of the RFC? >> >> Cheers >> Andreas > > > It's a totally different design concept (symmetric/left/right) and I've been going over the design implications before I responded. For instance, wouldn't this be a special case of method overloading? You're overloading according to a modifier, not the typing, but there is that to contend with. If someone defined `symmetric operator +` and `left operator +` which would be used? (My feeling is left as its more specific.)
You would not be allowed to provide both on the same class. A class can provide either no operator, or the left operator, or the right operator, or both. A "symmetric" operator is a shortcut to providing both left and right, but using the same implementation. A class with a "symmetric" operator cannot also have a "left" or "right" version of the same operator - this would be the same as two methods with the same name. However, if the parent class has a "symmetric" operator, you _can_ override the "left" and/or "right" version in the subclass. In that case, if the subclass provides "left", then the parent "symmetric" implementation would only be used for the "right" direction. I am writing "right" and "left" here, but perhaps "normal" and "inverted" would be less ambiguous. Also, perhaps instead of "symmetric" we could write "left right". Perhaps this code will clarify: class C { symmetric operator * (int $other) {..} # left operator * (int $other) {..} // -> Error, already defined. left operator - (int $other) {..} right operator - (int $other) {..} left operator / (float $other) {..} left operator % (D $other) {..} } class D { right operator % (C $other) {..} } (new C()) * 5; // -> symmetric operator * 5 * (new C()); // -> symmetric operator * (new C()) - 5; // -> left operator - 5 - (new C()); // -> right operator - (new C()) / 5; // -> left operator / 5 / (new C()); // -> Error, no operator provided. (new C()) % (new D()); // -> left operator % provided by C. (new D()) % (new C()); // -> Error, not supported. This means, the "right operator" will only ever be called if the left side does not provide a "left operator". Basically this is not so different from your RFC. Just instead of having to do a "if ($operandPos === OperandPosition::LeftSide) {..}" inside an implementation, we are providing two separate implementations.
> How would they be stored within the zend_class_entry? Since they would technically have the same name, something would need to happen for them to not be in the function table.
The right and left version would need to be distinguished somehow internally. I would say "left" is the default, and "right" has a modifier to its name. Perhaps for symmetric operators, we could store two distinct entries internally, if that makes things easier.
> > The public modifier is not required (as stated in the RFC), you can just add it if you want. Are you asking for `public operator` to produce a compile error?
That would be the idea. Perhaps others will disagree. Allowing the "public" introduces a meaningless choice that people can argue about in their code style, and have pointless git commits where they add or remove the modifier based on preference. Of course the same could be said about "public" in interface methods. -- Andreas

Chase Peeler

4 years ago
On Wed, Jan 5, 2022 at 3:24 PM Andreas Hennings <andreas@dqxtech.net> wrote:
> On Wed, 5 Jan 2022 at 20:11, Jordan LeDoux <jordan.ledoux@gmail.com> > wrote: > > > > > > > > On Wed, Jan 5, 2022 at 6:33 AM Andreas Hennings <andreas@dqxtech.net> > wrote: > >> > >> Hello Jordan, > >> do you have any thoughts about these symmetric/left/right modifiers, > >> to get rid of the $operandPos parameter? > >> > >> To me, the parameter could be the kind of thing where in hindsight we > >> ask "why?". > >> > >> Also, can we drop the "public" modifier, if we do a new version of the > RFC? > >> > >> Cheers > >> Andreas > > > > > > It's a totally different design concept (symmetric/left/right) and I've > been going over the design implications before I responded. For instance, > wouldn't this be a special case of method overloading? You're overloading > according to a modifier, not the typing, but there is that to contend with. > If someone defined `symmetric operator +` and `left operator +` which would > be used? (My feeling is left as its more specific.) > > You would not be allowed to provide both on the same class. > > A class can provide either no operator, or the left operator, or the > right operator, or both. > A "symmetric" operator is a shortcut to providing both left and right, > but using the same implementation. > A class with a "symmetric" operator cannot also have a "left" or > "right" version of the same operator - this would be the same as two > methods with the same name. > However, if the parent class has a "symmetric" operator, you _can_ > override the "left" and/or "right" version in the subclass. In that > case, if the subclass provides "left", then the parent "symmetric" > implementation would only be used for the "right" direction. > > I am writing "right" and "left" here, but perhaps "normal" and > "inverted" would be less ambiguous. > Also, perhaps instead of "symmetric" we could write "left right". > > Perhaps this code will clarify: > > class C { > symmetric operator * (int $other) {..} > # left operator * (int $other) {..} // -> Error, already defined. > left operator - (int $other) {..} > right operator - (int $other) {..} > left operator / (float $other) {..} > left operator % (D $other) {..} > } > > class D { > right operator % (C $other) {..} > } > > (new C()) * 5; // -> symmetric operator * > 5 * (new C()); // -> symmetric operator * > (new C()) - 5; // -> left operator - > 5 - (new C()); // -> right operator - > (new C()) / 5; // -> left operator / > 5 / (new C()); // -> Error, no operator provided. > > (new C()) % (new D()); // -> left operator % provided by C. > (new D()) % (new C()); // -> Error, not supported. > > This means, the "right operator" will only ever be called if the left > side does not provide a "left operator". > > Basically this is not so different from your RFC. > Just instead of having to do a "if ($operandPos === > OperandPosition::LeftSide) {..}" inside an implementation, we are > providing two separate implementations. > > > > How would they be stored within the zend_class_entry? Since they would > technically have the same name, something would need to happen for them to > not be in the function table. > > The right and left version would need to be distinguished somehow > internally. > I would say "left" is the default, and "right" has a modifier to its name. > Perhaps for symmetric operators, we could store two distinct entries > internally, if that makes things easier. > >
If nothing else symmetric could be handled by having the compiler replace it with left/right version that executes the same code
> > > > The public modifier is not required (as stated in the RFC), you can just > add it if you want. Are you asking for `public operator` to produce a > compile error? > > That would be the idea. > Perhaps others will disagree. > Allowing the "public" introduces a meaningless choice that people can > argue about in their code style, and have pointless git commits where > they add or remove the modifier based on preference. > Of course the same could be said about "public" in interface methods. > >
I personally don't like it when I don't have a consistent look to my code. One of the things that bother me is code like: function foo(){} protected function bar(){} function baz(){} I want everything to have a visibility indicator for visual consistency. That's why I always use public in my interface methods as well. So, even though it won't cause confusion if omitted for an operator, since it can only be public, I still think it should be allowed.
> -- Andreas > > > > > Jordan > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
-- Chase Peeler chasepeeler@gmail.com

Côme Chilliet

4 years ago
Hello, From the RFC:
> If the left operand produces a TypeError due to the parameter types listed in the implementation, the operation is not retried with the right operand and the error is instead returned immediately. This is to help developers encounter errors in their program logic as early as possible.
This feels wrong if I understand correctly. Let’s say I create a class A and I want to add support for "2 / new A()", which by default does not work. I can do class A { operator /(int $other, OperandPosition $operandPos): mixed { return "example"; } } Now someone wants to build on my work, and add a class B that supports "new A() / new B()". class B { operator /(A $other, OperandPosition $operandPos): mixed { return "example"; } } This will not work because it will first try A->{'/'}(B) that throws a TypeError? So it means what I was able to do for floats, cannot be done for my new classes afterwards? This is inconsistent I think. It also means it is not possible to extend any existing class with operators interacting with a new class, meaning you can only use operators among classes aware of each other, most likely from the same package/library. Also, this was stated already I think but I did not see an answer, from RFC example:
> class Matrix { > public function __construct(readonly public array $value) {} > > public operator *(Matrix $other, OperandPosition $operandPos): Number > { > if ($operandPos == OperandPosition::LeftSide) { > // Count of my columns needs to match > // count of $other rows > } else { > // Count of my rows needs to match > // count of $other columns > } > } > }
The second branch of the if is dead code, as $matrix1 * $matrix2 will always call $matrix1->{'*'}($matrix2, LeftSide) and never the other way around. This means that $operandPos is useless in all cases where operators are only typed againts the same type, and points to this design solution being wrong. There is «Why not interfaces?» in the FAQ, does that mean that operators cannot be added in interfaces? This is not stated clearly in the RFC. Also, it is not clear if operator overload will affect comparison operations used in core functions such as in_array, sort and so on. Does implementing operator == allows using in_array to find an object in an array? Which of these internal functions use == and which use ===, which is not overloadable? Does implementing operator <=> allows sorting of arrays containing my objects? Côme

Guilliam Xavier

4 years ago
Hi Jordan, Many thanks for all your replies. But did you miss Côme's mail (maybe because you weren't direct recipient)? Anyway, here it is again: On Tue, Jan 4, 2022 at 10:02 AM Côme Chilliet <come@chilliet.eu> wrote:
> Hello, > > From the RFC: > > > If the left operand produces a TypeError due to the parameter types > listed in the implementation, the operation is not retried with the right > operand and the error is instead returned immediately. This is to help > developers encounter errors in their program logic as early as possible. > > This feels wrong if I understand correctly. > > Let’s say I create a class A and I want to add support for "2 / new A()", > which by default does not work. > > I can do > class A > { > operator /(int $other, OperandPosition $operandPos): mixed > { > return "example"; > } > } > > Now someone wants to build on my work, and add a class B that supports > "new A() / new B()". > > class B > { > operator /(A $other, OperandPosition $operandPos): mixed > { > return "example"; > } > } > > This will not work because it will first try A->{'/'}(B) that throws a > TypeError? So it means what I was able to do for floats, cannot be done for > my new classes afterwards? This is inconsistent I think. It also means it > is not possible to extend any existing class with operators interacting > with a new class, meaning you can only use operators among classes aware of > each other, most likely from the same package/library. > > Also, this was stated already I think but I did not see an answer, from > RFC example: > > class Matrix { > > public function __construct(readonly public array $value) {} > > > > public operator *(Matrix $other, OperandPosition $operandPos): Number > > { > > if ($operandPos == OperandPosition::LeftSide) { > > // Count of my columns needs to match > > // count of $other rows > > } else { > > // Count of my rows needs to match > > // count of $other columns > > } > > } > > } > > The second branch of the if is dead code, as $matrix1 * $matrix2 will > always call $matrix1->{'*'}($matrix2, LeftSide) and never the other way > around. > This means that $operandPos is useless in all cases where operators are > only typed againts the same type, and points to this design solution being > wrong. > > There is «Why not interfaces?» in the FAQ, does that mean that operators > cannot be added in interfaces? This is not stated clearly in the RFC. > > Also, it is not clear if operator overload will affect comparison > operations used in core functions such as in_array, sort and so on. > Does implementing operator == allows using in_array to find an object in > an array? > Which of these internal functions use == and which use ===, which is not > overloadable? > Does implementing operator <=> allows sorting of arrays containing my > objects? > > Côme > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
I too would be interested in your answers to those. Best regards,
-- Guilliam Xavier

Jordan LeDoux

4 years ago
Apologies. I think I saw this, but then was distracted by other matters and lost track of it. This will not work because it will first try A->{'/'}(B) that throws a
>> TypeError? So it means what I was able to do for floats, cannot be done for >> my new classes afterwards? This is inconsistent I think. It also means it >> is not possible to extend any existing class with operators interacting >> with a new class, meaning you can only use operators among classes aware of >> each other, most likely from the same package/library. >> >
This is something covered by the future scope Polymorphic Handler Resolution. Essentially, to resolve this issue the types need to be checked for inheritance during the opline evaluation. That's something that would be a fairly easy to do in a very unperformant way, so I wanted to do that as a follow-up RFC in order to put real effort into making it something that would slow such oplines down too much.
> The second branch of the if is dead code, as $matrix1 * $matrix2 will >> always call $matrix1->{'*'}($matrix2, LeftSide) and never the other way >> around. >> This means that $operandPos is useless in all cases where operators are >> only typed againts the same type, and points to this design solution being >> wrong. >> >
Yes, for implementations only hinted against themselves, this would be true. The main other strategy would be to use a static implementation that takes both operands. This has a few downsides: 1. While it is still possible to access private and protected properties in such a case, doing so is awkward and not at all intuitive. 2. The boiler-plate code for checking types in order to figure out which side (left or right) is the called instance would be required for nearly every implementation. So some code was saved for some situations, but more code was added for all situations. 3. There is no situation in which these will *actually* be called statically. They are *only* called when an object instance exists, because an object instance is required in order to use with the operands. Such static implementations would only reduce code (in my opinion) if this was combined with method overloading, but that's a totally separate bag of controversy and difficulty.
> There is «Why not interfaces?» in the FAQ, does that mean that operators >> cannot be added in interfaces? This is not stated clearly in the RFC. >> >
No, these can be added to interfaces and abstract classes and final classes. They can even be added to enums actually. What that part is saying is that this RFC doesn't provide interfaces such as 'Stringable' for each of the operators.
> Also, it is not clear if operator overload will affect comparison >> operations used in core functions such as in_array, sort and so on. >> Does implementing operator == allows using in_array to find an object in >> an array? >> >
The in_array() function unfortunately uses its own comparison internally. This is something that I would want to add as part of the implementation step, yes, but it's not currently in the PR that's linked.
> Which of these internal functions use == and which use ===, which is not >> overloadable? >> >
Internally, these functions use neither, because they don't generate an opcode. They do the comparison on the ZVALs themselves. So each of them needs to be special cased.
> Does implementing operator <=> allows sorting of arrays containing my >> objects? >> >
The same as above applies. It doesn't actually generate an opcode, so it needs to be special cased. The good news is that this special casing would actually be pretty straightforward, since I also wrote helpers for things such as extensions to more easily handle these sorts of things. Jordan

Jordan LeDoux

4 years ago
Actually, on second glance, it seems that they make a call to zend_compare. So currently they would all work with an implementation of <=> but not ==. So I'd mostly just need to update the fast_equal_check_function. Jordan

A.L.E.C

4 years ago
On 03.01.2022 01:13, Jordan LeDoux wrote:
> https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > close on 2022-01-17.
- what is the performance impact? - why "[public] operator" and not "operator function"? - what about precedence, i.e. what happens with $a + $b * $c? there's no clear answer - why not allow the tilde operator to be used in a different context?, e.g. in PostgreSQL it is used as a regular expression match, e.g. $a ~ '^[a-z]+$' - the Implied Operators table does not mention ~= operation. - I don't like the Reflection API changes
-- Aleksander Machniak Kolab Groupware Developer [https://kolab.org] Roundcube Webmail Developer [https://roundcube.net] ---------------------------------------------------- PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

Jordan LeDoux

4 years ago
On Wed, Jan 5, 2022 at 12:26 AM Aleksander Machniak <alec@alec.pl> wrote:
> > - what is the performance impact? >
Each operator evaluation is very slightly better than the equivalent function call, since the VM call chain is very slightly simpler, but for most purposes you can think of the performance as being the same as a method call. This is quite a bit slower than most operator evaluations (which are lightning fast compared to function calls), however this *only* affects *objects* used with operators, which currently always result in a fatal error. So in that sense, there is no performance impact.
> - why "[public] operator" and not "operator function"? >
As many people in this thread have pointed out, it's possible for less experienced developers to do something truly devious with this (the same way that they can with __get, __set, or __toString). I wanted to avoid using the function keyword at all, because I wanted the code itself to mentally prepare the programmer to treat these differently than arbitrary functions. As I mentioned in a previous email, some will view this as wishy-washy, but that decision was done after consulting a specialist that I know in the field of Human Centered Design (the field of designing technology so that it is used correctly by design alone). That is, in truth, my largest reason for wanting the operator keyword, but it's also something that I invested nearly two months of study and consultation into. That makes it difficult for me to correctly convey that understanding to other people here via email, so I have mostly restricted my arguments to other things. I find myself in the odd position of not really being able to dump all of the research I did here for everyone else, because frankly, no one on this list *should* need to invest a week of studying to understand an RFC. So unfortunately, my best argument in favor of this is one I can't really make, but that *is* the reason that I went this route.
> - what about precedence, i.e. what happens with $a + $b * $c? there's no > clear answer >
Precedence is handled by the compiler and how it handles the opcodes. That's independent of any of the changes in this RFC, since the opcodes for all the operators are the same with or without operator overloads. So the precedence will be the same if $a, $b, and $c are ints or objects, even if we later (for some reason) changed the precedence of the operators.
> - why not allow the tilde operator to be used in a different context?, > e.g. in PostgreSQL it is used as a regular expression match, e.g. > $a ~ '^[a-z]+$' >
This would involve changing the opcode evaluation itself, instead of just implementing a do_operation handler in zend_class_entry. It would introduce much more surface for buggy behavior and be a great deal of additional effort for that single operator.
> - the Implied Operators table does not mention ~= operation. >
The ~= operator does not exist in PHP because the ~ operator is a unary operator in PHP (bitwise not). See: - https://www.php.net/manual/en/language.operators.assignment.php - https://3v4l.org/N9OYF
> - I don't like the Reflection API changes >
Please suggest changes then. It seems probable this will not pass in this incarnation, so knowing what things to improve before bringing it back would be helpful. Jordan

Jordan LeDoux

4 years ago
On Sun, Jan 2, 2022 at 4:13 PM Jordan LeDoux <jordan.ledoux@gmail.com> wrote:
> Hello internals, > > I've opened voting on > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will > close on 2022-01-17. > > To review past discussions on this RFC and the feature in general, please > refer to: > > - https://externals.io/message/116611 | Current RFC discussion > - https://externals.io/message/115764 | Initial RFC discussion > - https://externals.io/message/115648 | Pre-RFC discussion and > fact-finding > > Jordan >
Internals, The RFC has been declined with a vote of 21 in favor and 24 against. Jordan