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

Reply via email to