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

Reply via email to