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