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,
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
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
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
Greets,
Marco
Hey Andreas,
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§ion=1
$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
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§ion=1
https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)&action=edit§ion=1$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_TemplatesIn 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
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
Le 03/01/2022 à 17:12, Larry Garfield a écrit :
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
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
I forgot to answer to that, specifically. I'd much more prefer to have
an explicitequals(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
I forgot to answer to that, specifically. I'd much more prefer to have
an explicitequals(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
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
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
But why shouldn't it be
==
... these are valid use cases for a
comparison; equally, vector additions ($vector1 + $vector2) are quite
commonplace in mathematics
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
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
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 nameBut why shouldn't it be
==
... these are valid use cases for a comparison; equally, vector additions ($vector1 + $vector2) are quite commonplace in mathematicsMarco'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 fully agree:
Any language should aim at doing math well, natively.
You should not have to want to go to another language for
calculations. If this is the case, the language is incomplete.
And I think modern PHP aims to be or become a "complete" language.
If people currently don't use the language for math problems, this
says more about the language than about the people.
There is a potential here to enable new packages that solve new
problems, that people would have done in another language in the past.
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 likesymmetric operator
to show that an operator can be in either position. [..]
That was me :)
I suppose you are going to reply directly to that message.
[..] 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.
Tbh, I had to warm up to the idea myself.
At first I only thought of all the potential problems that were
already mentioned here.
The main problem was I only thought of existing code, not of new code
to solve new problems.
-- Andreas
Jordan
Hey Jordan,
On Tue, Jan 4, 2022 at 3:23 AM Jordan LeDoux jordan.ledoux@gmail.com
wrote:
But why shouldn't it be
==
... these are valid use cases for a
comparison; equally, vector additions ($vector1 + $vector2) are quite
commonplace in mathematicsMarco'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 know you're picking on details here, but believe me, I'm serious when I'm
saying "use matlab".
In fact, I did work on financial products where (from Java PHP and Python)
we used the MATLAB Compiler to create binaries and dynamic libraries, which
would then do the heavy lifting around the financial models that were in
use.
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 magicThe 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 )
As for the install count of markbaker/matrix (which, by the way, is an
excellent library), please do keep in mind that those are directly
coupled with phpoffice/phpspreadsheet (
https://packagist.org/packages/phpoffice/phpspreadsheet ). In fact, all OSS
dependents of this library are just forks of phpspreadsheets:
https://packagist.org/packages/markbaker/matrix/dependents?order_by=downloads&page=1
This absolutely is not to diminish Mark's work, which is stellar (and
frankly, I told him multiple times that he should just get paid for it,
since spreadsheets run the frikken world), but to show that this is indeed
a niche use-case.
Marco Pivetta
I know you're picking on details here, but believe me, I'm serious when
I'm saying "use matlab".In fact, I did work on financial products where (from Java PHP and Python)
we used the MATLAB Compiler to create binaries and dynamic libraries, which
would then do the heavy lifting around the financial models that were in
use.
"Use an external tool that already exists to accomplish this task" is not,
in my view, a legitimate reason to reject a feature from the language on
its own. I see it as a relevant additional detail justifying a rejection,
given that the rejection is for other reasons. That is, it's more of a
justification for "this is why the rejection isn't that bad" than it is
"this is why it should be rejected". You can literally use this argument
to reject any feature, which means that it's application is entirely
arbitrary.
Let me phrase this another way: if I came later with a different RFC that
has a main use case of mathematics, would you reject that as well on the
basis that mathematics should not be well done in PHP? I'm not asking
rhetorically, I'm legitimately wanting an answer to this question. Do I
need to find and demonstrate non-mathematics use cases on future RFCs in
order to have them seriously considered?
Mathematics is not niche. I simply disagree on that. And it sort of seems
that you do as well, since immediately before saying mathematics was
niche you also said that spreadsheets run the world.
If this RFC is rejected, as seems likely at this point barring some changed
minds, I can certainly accept that. Several of the yes votes so far on this
RFC also told me they would likely vote no out-of-hand when I first
discussed this RFC back in August, but I was able to listen to their
concerns, continue improving the RFC, and also present my arguments for why
it is an important and impactful feature to them. They provided me with
concrete objections that I could consider and see if there was a way for me
to address and incorporate into the feature design, and it made the RFC
better. But virtually all of the no votes so far declined to participate in
any of the three threads that I had soliciting feedback, in the chat
discussions over the last 5 months, or even in this thread.
I have one actionable bit of feedback so far from Nikita, suggesting that
the RFC would be improved with different syntax.
I am at a total loss at this point as to how to proceed with other
contributions I would like to make to PHP in the future, as the only
conclusion I can draw right now is that math improvements simply aren't
important or even wanted.
Can I expect this for other math related RFCs, or is this dogmatic about
operator overload specifically? I have no clue, and at the very least, I
would like to learn that.
Jordan
On Wed, Jan 5, 2022 at 3:19 AM Jordan LeDoux jordan.ledoux@gmail.com
wrote:
Can I expect this for other math related RFCs, or is this dogmatic about
operator overload specifically? I have no clue, and at the very least, I
would like to learn that.
It's in the name: "overload"
You can certainly expect pushback from my end for any implementation
thereof.
As I mentioned above, your RFC is well implemented: I oppose the feature,
not the implementation.
Greets,
Marco Pivetta
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!
Hey Andreas,
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§ion=1
$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?
Marco Pivetta
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
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
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?
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
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-findingJordan
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
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-findingJordan
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.
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
Hi Nikita,
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-findingJordan
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
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
Hey Jordan.
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 |
+---------------------------------------------------------------------+
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.
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
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
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
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::RightSideI 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
AndreasHello 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
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
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
AndreasIt'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 +
andleft 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
Jordan
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
AndreasIt'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 definedsymmetric operator +
andleft 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 forpublic 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
--
To unsubscribe, visit: https://www.php.net/unsub.php
--
Chase Peeler
chasepeeler@gmail.com
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
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:
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
--
To unsubscribe, visit: https://www.php.net/unsub.php
I too would be interested in your answers to those.
Best regards,
--
Guilliam Xavier
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:
- While it is still possible to access private and protected properties in
such a case, doing so is awkward and not at all intuitive. - 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. - 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
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
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
- 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:
- 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
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-findingJordan
Internals,
The RFC has been declined with a vote of 21 in favor and 24 against.
Jordan