Hi internals,
based on the discussions here (https://externals.io/message/108300) and here
(https://github.com/php/php-src/pull/5156), I have created a proper RFC for
userspace operator overloading:
https://wiki.php.net/rfc/userspace_operator_overloading
The main differences to my original concept, is the removed __compare()
method (comparison overloading is a complex topic and should be handled in a
different RFC) and the possibility to signal that the operator handler does
not support the given types (by typehints or returning a special value).
This way, only one of both objects has to know about the other type. This
should expand the use case of operator overloading compared to my old
concept.
What do you think about the RFC?
Some discussion points, I can think of, would be the naming of the methods
(maybe naming them after the operator symbol and not the arithmetical
operation they represent, e.g. __plus instead of __add) or putting the
methods inside of interfaces like done for ArrayAccess (But I dont see any
advantage in doing so, as it is very difficult grouping different operators
in a single interface usefully. Also, operators can accept and return
different types, so there is no real common interface between classes you
could rely on).
Furthermore, maybe the idea of allowing operator overloading in general
should be discussed as it is sometimes considered an anti-pattern (e.g. the
usage of '<<' for outputting a string in C++). On the other hand there are
many languages and libraries where operator overloading is used successfully
(e.g. numpy in Python).
Regards,
Jan Böhmer
Hi internals,
What do you think about the RFC?
"If an operator is used with an object that does not overload this
operator, an NOTICE is triggered, which gives the user a hint about the
method that has to be overloaded. For backward compatibility objects,
which do not overload the operator, are converted to integer 1 (current
behavior)."
Noooo :( Notices are the enemy of all that is great and good.
If operator overloading is going to become a first-class feature then it
should be treated as such, and attempting overloading operators on
objects that don't have the relevant method available should trigger an
Error.
--
Mark Randall
marandall@php.net
Some discussion points, I can think of, would be the naming of the
methods
(maybe naming them after the operator symbol and not the arithmetical
operation they represent, e.g. __plus instead of __add) or putting the
methods inside of interfaces like done for ArrayAccess
As I mentioned earlier [1] I think the answers to both of these questions depend on a fundamental philosophical question about what you're overloading:
-
The groups of operations which exist for built-in types e.g. the arithmetic operations overloaded by GMP; or string operations overloaded by an object with enhanced Unicode functionality.
-
The individual symbolic operators, with no intrinsic meaning - e.g. overloading . for concatenation on strings but dot-product for vectors; or a DSL overloading << and >> for "into" and "out of".
I think it would benefit the RFC to take a stance on that question, and build the feature around it.
[1] https://news-web.php.net/php.internals/108347
--
Rowan Tommins
[IMSoP]
Hi!
- The individual symbolic operators, with no intrinsic meaning - e.g.
overloading . for concatenation on strings but dot-product for
vectors; or a DSL overloading << and >> for "into" and "out of".
Please no. I know it looks fancy and some languages love it, but for a
person not in on the joke reading such code is a nightmare - it's
basically gibberish until you learn all the types and which meaning each
of the types assigns to operators. I mean, I could be maybe talked into
having users overload "+" to mean "add some kind of objects that are
like numbers but not actually integers" because my intuition still works
there even if I don't know the exact type. But if someone thinks it's
super-cool to override + to make it add element to the cache object -
please no. It's way worse than just using plain old ->addObject(). You
may save couple of keystrokes and make your code completely
incomprehensible for people who come after you.
I know DSLs have its niche but I seriously doubt average PHP user
implements a lot of DSLs that are required to work natively on PHP engine.
Stas Malyshev
smalyshev@gmail.com
Hi!
- The individual symbolic operators, with no intrinsic meaning - e.g.
overloading . for concatenation on strings but dot-product for
vectors; or a DSL overloading << and >> for "into" and "out of".Please no. I know it looks fancy and some languages love it, but for a
person not in on the joke reading such code is a nightmare
:) I know you're not alone in that feeling. If it turns out this is the majority view, I think it answers a couple of open questions:
Overload methods should definitely be named after operations, not symbols, to remind people they are implementing addition, not giving new meaning to +
They should probably be grouped into interfaces, which this RFC has so far resisted. How often does it make sense for a type to support addition but not subtraction, or multiplication but not division? Even more clearly, if a type claims to implement bitwise OR but not bitwise AND, NOT, and XOR, something is definitely fishy.
You can't stop people using overloading DSL-style, but you can make it obvious that it's not the intention of the feature (if we agree that it's not the intention; maybe some people here are really hoping to use it that way?)
Regards,
--
Rowan Tommins
[IMSoP]
Hi internals,
based on the discussions here (https://externals.io/message/108300) and
here
(https://github.com/php/php-src/pull/5156), I have created a proper RFC
for
userspace operator overloading:
https://wiki.php.net/rfc/userspace_operator_overloadingThe main differences to my original concept, is the removed __compare()
method (comparison overloading is a complex topic and should be handled in
a
different RFC) and the possibility to signal that the operator handler does
not support the given types (by typehints or returning a special value).
This way, only one of both objects has to know about the other type. This
should expand the use case of operator overloading compared to my old
concept.What do you think about the RFC?
Some discussion points, I can think of, would be the naming of the methods
(maybe naming them after the operator symbol and not the arithmetical
operation they represent, e.g. __plus instead of __add) or putting the
methods inside of interfaces like done for ArrayAccess (But I don’t see any
advantage in doing so, as it is very difficult grouping different operators
in a single interface usefully. Also, operators can accept and return
different types, so there is no real common interface between classes you
could rely on).
Furthermore, maybe the idea of allowing operator overloading in general
should be discussed as it is sometimes considered an anti-pattern (e.g. the
usage of '<<' for outputting a string in C++). On the other hand there are
many languages and libraries where operator overloading is used
successfully
(e.g. numpy in Python).Regards,
Jan Böhmer--
Just as a FYI, I'll vote No to this RFC regardless of arguments as I find
operator
(and method) overloading a bad idea in general. Moreover, the only ones I am
a tiny bit less reluctant to overload are the mathematical operators.
However, one of the major reasons to want to have this seems to add better
support for Matrices and Complex number which can already be done at the
extension/engine level.
Best regards
George P. Banyard
jan.h.boehmer@gmx.de wrote:
based on the discussions here (https://externals.io/message/108300)
and here (https://github.com/php/php-src/pull/5156), I have created a
proper RFC for userspace operator overloading:
https://wiki.php.net/rfc/userspace_operator_overloadingThe main differences to my original concept, is the removed
__compare() method (comparison overloading is a complex topic and
should be handled in a different RFC) and the possibility to signal
that the operator handler does not support the given types (by
typehints or returning a special value). This way, only one of both
objects has to know about the other type. This should expand the use
case of operator overloading compared to my old concept.What do you think about the RFC?
I very much appreciate this initiative, as well as that of Patricio/Sara,
and I agree with what people have said about the importance of realistic
use-cases for something like this.
Our company produces ads, and the price calculation is quite complex, involving
the size of the ads, various discounts, etc. We're using objects for these
calculations, to ensure we don't mix up units.
Here are some examples:
$price_per_column_mm = new PricePerColumnMm(1234); // Price = 1234 * number
of columns * height in mm
$height = new LengthInMm(100);
$price_per_column = $price_per_column_mm->multiply($height); // Returns a
PricePerColumn object
$columns = new Column(4);
$price = $price_per_column->multiply($columns); // Returns a Money object
It gets more complicated, when we add in discounts:
$discount_percentage = new Percentage(10);
$end_user_price = $price->subtract($discount_percentage); // Price = Price
- (100-discount percentage)/100
These are just a few examples, but it shows the advantage of using strongly
typed objects in expressions, so that we don't do nonsensical things like
multiplying two Money objects, or try to use a PricePerColumn object as a
Money object.
As you can probably tell, having all those "add", "subtract", "multiply",
etc. method calls makes it hard to understand what is going on, and even
if things happen in the right order, because when you use method calls, you
can't change the order of execution without breaking an expression up into
multiple expressions: It always happens from left to right.
In order to support code like the above, we'd need to be able to define the
following operators:
PricePerColumnMm * LengthInMm -> PricePerColumn
PricePerColumn * Column -> Money
Money - Percentage -> Money
This comes in addition to the usual operators that takes the same type on
both sides, such as Money + Money, etc.
Please note the last one, which is a non-commutative operation (Money - Percentage
!= Percentage - Money), which wouldn't work in Patricio's proposal, unless
the "Associativity" section is rewritten to handle this in a different way.
I like Patricio's proposal in that it uses ordinary, non-static methods,
but unfortunately, it then runs into this problem, which seems to favour
Jan's proposal.
With regard to the mechanism for handling this (interfaces, magic methods,
or a magic __overload() method), or Sara's proposal for registering overloads,
using overload_type(), none of that are deal-breakers for me.
Still, as we already have "magic methods" for other things (and interfaces,
yes, I know), I'd lean towards implementing these as magic methods, which
lets a developer only implement the operations that makes sense in their
domain.
Also, I think we should distinguish between non-assign and assign operators,
giving them different method names, because some types may be expensive to
copy, for example a large matrix, so $matrix *= $vector could be way more
efficient than $matrix = $matrix * $vector.
Regards,
Terje