Am 02.03.2020 um 15:30 schrieb Nikita Popov:
On Thu, Feb 27, 2020 at 9:43 PM <jan.h.boeh...@gmx.de
<mailto:jan.h.boeh...@gmx.de>> wrote:
On 15/02/2020 22:05, jan.h.boeh...@gmx.de
<mailto: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).
Regards,
Jan Böhmer