Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:108722 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 78190 invoked from network); 22 Feb 2020 17:47:47 -0000 Received: from unknown (HELO localhost.localdomain) (76.75.200.58) by pb1.pair.com with SMTP; 22 Feb 2020 17:47:47 -0000 To: internals@lists.php.net Date: Sat, 22 Feb 2020 16:04:15 +0000 (UTC) Message-ID: <479ea7da1e2828d7b7b93f6f1768@news.php.net> References: <003801d5e44c$169700e0$43c502a0$@gmx.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=iso-8859-1; format=flowed X-Newsreader: JetBrains Omea Reader 1098.1 X-Posted-By: 81.166.177.73 Subject: Re: [RFC] Userspace operator overloading From: terje.slettebo@sandefjordbredband.net (=?iso-8859-1?Q?Terje=20Sletteb=f8?=) jan.h.boehmer@gmx.de wrote: > based on the discussions here (https://externals.io/message/108300) > and here (https://github.com/php/php-src/pull/5156), I have created a > proper RFC for userspace operator overloading: > https://wiki.php.net/rfc/userspace_operator_overloading > > The main differences to my original concept, is the removed > __compare() method (comparison overloading is a complex topic and > should be handled in a different RFC) and the possibility to signal > that the operator handler does not support the given types (by > typehints or returning a special value). This way, only one of both > objects has to know about the other type. This should expand the use > case of operator overloading compared to my old concept. > > What do you think about the RFC? I very much appreciate this initiative, as well as that of Patricio/Sara, and I agree with what people have said about the importance of realistic use-cases for something like this. Our company produces ads, and the price calculation is quite complex, involving the size of the ads, various discounts, etc. We're using objects for these calculations, to ensure we don't mix up units. Here are some examples: $price_per_column_mm = new PricePerColumnMm(1234); // Price = 1234 * number of columns * height in mm $height = new LengthInMm(100); $price_per_column = $price_per_column_mm->multiply($height); // Returns a PricePerColumn object $columns = new Column(4); $price = $price_per_column->multiply($columns); // Returns a Money object It gets more complicated, when we add in discounts: $discount_percentage = new Percentage(10); $end_user_price = $price->subtract($discount_percentage); // Price = Price * (100-discount percentage)/100 These are just a few examples, but it shows the advantage of using strongly typed objects in expressions, so that we don't do nonsensical things like multiplying two Money objects, or try to use a PricePerColumn object as a Money object. As you can probably tell, having all those "add", "subtract", "multiply", etc. method calls makes it hard to understand what is going on, and even if things happen in the right order, because when you use method calls, you can't change the order of execution without breaking an expression up into multiple expressions: It always happens from left to right. In order to support code like the above, we'd need to be able to define the following operators: PricePerColumnMm * LengthInMm -> PricePerColumn PricePerColumn * Column -> Money Money - Percentage -> Money This comes in addition to the usual operators that takes the same type on both sides, such as Money + Money, etc. Please note the last one, which is a non-commutative operation (Money - Percentage != Percentage - Money), which wouldn't work in Patricio's proposal, unless the "Associativity" section is rewritten to handle this in a different way. I like Patricio's proposal in that it uses ordinary, non-static methods, but unfortunately, it then runs into this problem, which seems to favour Jan's proposal. With regard to the mechanism for handling this (interfaces, magic methods, or a magic __overload() method), or Sara's proposal for registering overloads, using overload_type(), none of that are deal-breakers for me. Still, as we already have "magic methods" for other things (and interfaces, yes, I know), I'd lean towards implementing these as magic methods, which lets a developer only implement the operations that makes sense in their domain. Also, I think we should distinguish between non-assign and assign operators, giving them different method names, because some types may be expensive to copy, for example a large matrix, so $matrix *= $vector could be way more efficient than $matrix = $matrix * $vector. Regards, Terje