On Fri, Mar 6, 2020 at 9:17 AM Jan Böhmer <jan.h.boeh...@gmx.de> wrote:
> Am 02.03.2020 um 15:30 schrieb Nikita Popov: > > On Thu, Feb 27, 2020 at 9:43 PM <jan.h.boeh...@gmx.de> wrote: > >> On 15/02/2020 22:05, jan.h.boeh...@gmx.de wrote: >> > 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 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 >> >> I have changed the proposed names for the bitshift handlers to '__lshift' >> and '__rshift' (instead of __sl and __sr) to make more clear what operator >> is handled by the method (also the method names are now mostly consistent >> with the Python equivalents). >> >> How many of you would prefer a interface solution for operator >> overloading? >> I wonder if the RFC voting should include the option to choose between >> either the magic method approach (with the syntax proposed in the current >> RFC version) or using interfaces. >> For an interface version I would suggest these interfaces: >> ArithmeticOperators (implements +, -, *, /), PowOperator (**), >> ModuloOperator (%), ConcatOperator (.) and BitwiseOperators (~, &, |, ^, >> <<, >> >>). >> What would be appropriate names for the interface methods? If we just name >> them after the operation (like add()), it will become difficult to >> integrate >> these interfaces into existing code, as add() is already a used function >> name in many cases, but uses a different signature (non-static with one >> argument, whereas the interface needs a static one with two arguments). >> >> Regards, >> Jan >> > > Some notes: > > Naming: If we're already going for more verbose names, I'd prefer > __shiftLeft over __lshift. It may also make sense to use __bitwiseNot > rather than __not, as it's currently not obviously whether it overloads the > ~ or the ! operator. Similarly __bitwiseAnd could be better than __and, to > make it clear that this is about & and not && or "and". Same for the other > bitwise operators. > > > -$a is interpreted as (-1 * $a). > > As you mention this, for completeness: +$a is interpreted as (1 * $a). > > It may be worthwhile to give a more explicit list for a) operators that > can be indirectly overloaded (and their desugaring) and b) operators that > cannot be overloaded (like the boolean operators, comparison operators, > instanceof, probably others). > > > If the operator handler function declares typehints (e.g. public static > function __add(Vector3 $lhs, int $rhs)), the function handler is not called > if the operand types do not match the signature, and the other operand's > handler is tried to call. > > I'm somewhat skeptical about this. This smells of method overloading, and > we don't do method overloading. There is no other place in PHP that would > perform dispatching based on the method signature, even if in this case > it's not a choice between multiple methods on the same class, but rather > multiple methods on different classes. Some of the usual problems of method > overloading don't apply here (in particular, one could make a reasonable > argument that the method on the first object should be chosen, even if > there is a more specific signature available on the second object), but I'm > skeptical about introducing this kind of special case in the language > specification. > > Regarding interfaces: I pretty strongly think that using interfaces for > this purpose is not a good idea. Interfaces are about contracts, and there > is no way (within the current limitations of PHP's type system) to define a > useful contract here. > > I think others have already expanded on why it's not possible to group > operators in a meaningful way. The DateTime example is probably the most > pertinent there, in that DateTime + DateTime is illegal, while DateTime - > DateTime is legal. If we can't even require both + and - as part of one > interface, there is very little we can require. > > However, even if we split each method into it's own interface, what we'll > be left with is something like > > interface Add { > public static function add($a, $b); > } > > What does this interface tell us? Pretty much nothing. It tells us that > there probably exists at least one type with which this object can be > added, but not what that type actually is or what the result type would be. > There is no way to build code on the contract of "the object is addable > with *something*". > > To make this useful, the interface would actually have to look something > like this: > > interface Add<T1, T2, T3> { > public static function add(T1 $a, T2 $b): T3; > } > > and then DateTime would implement something like this: > > class DateTime implements > Sub<DateTime, DateTime, DateInterval>, > Add<DateTime, DateInterval, DateTime> { ... } > > That's a contract you could build code on, though it would be rather > cumbersome. But in the absence of generic types, having an Add interface is > not useful. > > The way I see operator overloading being used in PHP, I would expect code > to be typed against specific classes, not supported operators. The vast > majority of code will be interested in accepting a Money, not an Add. > > Regards, > Nikita > > I have changed the names of the handlers like you suggested and added two > tables which show what operators can be overloaded indirectly and which can > not be overloaded at all. Thank you for your suggestion. > > I agree with your opinion with on interfaces, I think using magic methods > is much more reasonable. > > Your point about the "smell of method overloading" is interesting. In my > opinion this mechanism makes it a bit easier to use operator overloading as > you dont have to do tedious typecheckings on your own in simple cases. But > I agree that this behavior is a bit odd compared to other PHP features. > > That feature is not really needed for operator overloading, as you can use > the PHP_UNKNOWN_OPERAND_TYPES const for signaling that the handler does not > support the given types. If you (the internals developers) says that this > does not fit into the concept of PHP, I will remove it from my RFC (or put > it in a separate votation, if wished). > Does anyone else have thoughts on the ability to specify the supported types in the signature? Nikita