# [RFC] Userspace operator overloading

- Thread: https://externals.io/message/108788
- Original archive: https://news-web.php.net/php.internals/108788
- Started by jan.h.boehmer@gmx.de on 2020-02-27T20:42:52+00:00
- Messages: 13
- RFC: Userspace Operator overloading (Declined) — https://externals.io/rfc/userspace_operator_overloading.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #108788 — jan.h.boehmer@gmx.de — 2020-02-27T20:42:52+00:00

URL: https://externals.io/message/108788#108788

> 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

---

## #108798 — Andrea Faulds — 2020-03-01T17:11:52+00:00

URL: https://externals.io/message/108788#108798 · In reply to: #108788

Hi Jan,

jan.h.boehmer@gmx.de wrote:
> 
> 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 (~, &, |, ^, <<,
> > […]

I have probably already said this on this list; I like the idea of 
interfaces as they discourage abuse. I'm not sure if I like those 
particular groupings however:

* For integer-like types maybe supporting + - * without / would make 
sense, since / is de-facto floating-point division right now
* Maybe / and ** should be grouped?
* It's a shame `intdiv()` isn't an operator, as it would be grouped with % 
I guess… perhaps % and / should be grouped, if we expect types to use / 
for pure integer division? (This kinda contradicts my earlier point)
* If the bitwise operators were used for sets (Python does this), you 
would use & | ^ but not ~ << >>

Maybe the typeclasses in the Haskell Prelude can provide some 
inspiration 
(https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html has 
Num, Integral, Fractional, Floating), but this should probably be 
fashioned after imagined use-cases: what kinds of types can we imagine 
using operator overloading, and what sets of operators would they 
implement? From those, reasonable groups could be formed, I guess.

> 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).

I had wanted just ->add(), but you raise a good point that existing code 
would probably conflict.

An easy solution could be to prefix all the methods with “operator”? 
->operatorAdd(), ->operatorMinus(), ->operatorDivide(), 
->operatorBitwiseNot() (to distinguish it from hypothetical future 
LogicalNot/BooleanNot) etc…

Thanks,
Andrea

---

## #108805 — Nikita Popov — 2020-03-02T14:30:03+00:00

URL: https://externals.io/message/108788#108805 · In reply to: #108788

> 
> > […]
> here
> > […]
> for
> > […]
> in
> a
> > […]
> does
> > […]
> methods
> > […]
> any
> > […]
> operators
> > […]
> the
> > […]
> are
> > […]
> successfully
> > […]
>
> 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

---

## #108809 — Rowan Tommins — 2020-03-02T14:55:53+00:00

URL: https://externals.io/message/108788#108809 · In reply to: #108805

>
> 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.
>


In my opinion, not being able to overload both + and - is a sign that the
class is "abusing" operator overloading, in that it is using it to build a
domain-specific language, rather than truly overloading *operations*.

An overload of "DateTime - DateTime" wouldn't represent "subtraction" for
precisely the reason that "DateTime + DateTime" doesn't make sense -
DateTime doesn't follow arithmetical logic. Instead, it would represent a
domain-specific definition of "difference". A more "correct" operator
overloading would be DateTime +  DateInterval, and its inverse DateTime -
DateInterval.

The common consensus seems to be that we don't want domain-specific
overloading (the example of << and >> in C++ has been mentioned several
times). If that's the case, then an interface that prevents you
implementing DateTime - DateTime seems perfectly legitimate.

Regards,
-- 
Rowan Tommins
[IMSoP]

---

## #108811 — Mike Schinkel — 2020-03-02T16:56:13+00:00

URL: https://externals.io/message/108788#108811 · In reply to: #108805

> 
> 
> 
> 
> > […]
> 
> Naming: If we're already going for more verbose names, I'd prefer
> __shiftLeft over __lshift.

Yes, please.

-Mike

---

## #108812 — terje.slettebo@sandefjordbredband.net — 2020-03-02T17:23:19+00:00

URL: https://externals.io/message/108788#108812 · In reply to: #108788

> 
> 
> 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 (~, &, |,
> ^, <<, >>> ).

+1 for magic methods.

There's too many domains where only a few of the operators makes sense and 
groupings like ArithmeticOperators would not make sense, not at least one 
of the motivating examples of this proposal: Being able to write a Money 
class and associated functionality.

Adding and subtracting money makes sense, but it makes no sense to multiply 
or divide them, and this is just *one* domain.

Please, let's not go there and artificially limit the possibilities of future 
developers.

For example, for me, it's quite natural to be able to write code like this:

$total_discount = $total * $discount;

Here, $total_discount and $total is Money, while $discount is a Percentage, 
so we should be able to define a method that allows you to multiply Money 
with Percentage, and return a Money object. Adding Money and Percentage would 
make no sense, but multiplying them does.

Regards,

Terje

---

## #108817 — Rowan Tommins — 2020-03-03T09:52:52+00:00

URL: https://externals.io/message/108788#108817 · In reply to: #108812

Hi Terje,

I think both of your examples are compatible with the idea of grouped
operators, as long as we don't constrain a type to implement all operators
with the same right-hand side.


On Mon, 2 Mar 2020 at 17:23, Terje Slettebø <
terje.slettebo@sandefjordbredband.net> wrote:

>
> Adding and subtracting money makes sense, but it makes no sense to
> multiply
> or divide them, and this is just *one* domain.
>


This is true, but it does make sense to multiply and divide them by
numbers, for instance $totalPrice = $unitPrice * $quantity

In general, if $a + $a represents addition, then $a * 2 should logically
give the same result. As has been raised, there is an argument for keeping
/ separate, for types that cannot handle fractional operations; it would be
a matter of choice whether $price / 2 should be supported because it would
lead to fractional pennies/cents.



> $total_discount = $total * $discount;
>
> Here, $total_discount and $total is Money, while $discount is a
> Percentage,
> so we should be able to define a method that allows you to multiply Money
> with Percentage, and return a Money object. Adding Money and Percentage
> would
> make no sense, but multiplying them does.
>


If this operator was implemented in isolation, it would again be drifting
into domain-specific language territory. (I don't personally have a problem
with that, but many people vocally object to it.)

However, it could easily be made part of a consistent set of arithmetic
operators:

On class Money:

Money + Money => Money
Money - Money => Money
Money * int => Money
Money / int => Money
int * Money => Money
int / Money => Error
Money * Percentage => Money
Money / Percentage => Money
Percentage * Money => Money
Percentage / Money => Error

Optionally, on class Percentage:

Percentage + Percentage => Percentage
Percentage - Percentage => Percentage
Percentage* int => Percentage
Percentage / int => Percentage



Regards,
-- 
Rowan Tommins
[IMSoP]

---

## #108882 — jan.h.boehmer@gmx.de — 2020-03-06T08:17:17+00:00

URL: https://externals.io/message/108788#108882 · In reply to: #108805

Am 02.03.2020 um 15:30 schrieb Nikita Popov:
> On Thu, Feb 27, 2020 at 9:43 PM <jan.h.boehmer@gmx.de
> <mailto:jan.h.boehmer@gmx.de>> wrote:
>
>     On 15/02/2020 22:05, jan.h.boehmer@gmx.de
>     <mailto:jan.h.boehmer@gmx.de> wrote:
> > […]
>     (https://externals.io/message/108300) and
>     here
> > […]
>     proper RFC
>     for
> > […]
>     __compare()
> > […]
>     handled in
>     a
> > […]
>     handler
>     does
> > […]
>     value).
> > […]
>     type. This
> > […]
>     my old
> > […]
>     the methods
> > […]
>     arithmetical
> > […]
>     putting the
> > […]
>     don’t see
>     any
> > […]
>     operators
> > […]
>     return
> > […]
>     classes you
> > […]
>     general
> > […]
>     anti-pattern (e.g.
>     the
> > […]
>     there are
> > […]
>     successfully
> > […]
>
>     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.
>
> > […]
>
> 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).
>
> > […]
> 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

---

## #108968 — Nikita Popov — 2020-03-11T09:22:03+00:00

URL: https://externals.io/message/108788#108968 · In reply to: #108882

> Am 02.03.2020 um 15:30 schrieb Nikita Popov:
>
> 
>
> > […]
>
> 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.
>
> > […]
>
> 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).
>
> > […]
> 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

---

## #108969 — Christoph M. Becker — 2020-03-11T09:50:31+00:00

URL: https://externals.io/message/108788#108969 · In reply to: #108968

> 
>
> > […]
>
> Does anyone else have thoughts on the ability to specify the supported
> types in the signature?

I agree that we should not introduce (this special case of) overloaded
functions.

Thanks,
Christoph

---

## #108991 — jan.h.boehmer@gmx.de — 2020-03-12T08:31:38+00:00

URL: https://externals.io/message/108788#108991 · In reply to: #108969

> 
>
> > […]
> I agree that we should not introduce (this special case of) overloaded
> functions.
>
> Thanks,
> Christoph

I have thought about this the last days, and I agree with you. Therefore
I have removed this feature from the RFC.

If PHP perhaps gets real function overloading someday, we could consider
then to integrate operator overloading like in other languages (a
separate handler for each type combination), which would be much more
useful than the mechanism I had proposed.

If there are no other open discussion points, I would put the RFC to
voting in the next days.


Thank you for your feedback,

Jan

---

## #108993 — Rowan Tommins — 2020-03-12T10:06:14+00:00

URL: https://externals.io/message/108788#108993 · In reply to: #108991

> 
> > […]
>
> I have thought about this the last days, and I agree with you. Therefore
> I have removed this feature from the RFC.
>


I've been thinking about it as well, and am not sure what happens _without_
it in a case like this:

# initial class, only overloads + on instances of itself
class A {
    public static function __add(A $lhs, A $rhs) {
         // ...
    }
}

# in some third-party library
class B {
    public static function __add(A|B $lhs, A|B $rhs) {
         // ...
    }
}

$a = new A;
$b = new B;
var_dump($b + $a); # calls B::__add($b, $a); OK
var_dump($a + $b); # calls A::__add($a, $b), which is a TypeError

If the engine doesn't catch the TypeError, it will never try B::__add($a,
$b) in the second case, so presumably the TypeError will just be thrown to
the user, which doesn't seem very helpful.

Perhaps the simplest solution is to have a restriction that the operator
overloads _must not_ specify types in their signature?

Regards,
-- 
Rowan Tommins
[IMSoP]

---

## #108999 — jan.h.boehmer@gmx.de — 2020-03-13T11:05:50+00:00

URL: https://externals.io/message/108788#108999 · In reply to: #108968

> 
>
> > […]
>
>
> I've been thinking about it as well, and am not sure what happens _without_
> it in a case like this:
>
> # initial class, only overloads + on instances of itself
> class A {
>    public static function __add(A $lhs, A $rhs) {
>         // ...
>    }
> }
>
> # in some third-party library
> class B {
>    public static function __add(A|B $lhs, A|B $rhs) {
>         // ...
>    }
> }
>
> $a =3D new A;
> $b =3D new B;
> var_dump($b + $a); # calls B::__add($b, $a); OK
> var_dump($a + $b); # calls A::__add($a, $b), which is a TypeError
>
> If the engine doesn't catch the TypeError, it will never try B::__add($a,
> $b) in the second case, so presumably the TypeError will just be thrown to
> the user, which doesn't seem very helpful.
>
> Perhaps the simplest solution is to have a restriction that the operator
> overloads _must not_ specify types in their signature?
>
> Regards,
>
> Rowan Tommins

I have implmented your suggestion. If an operator handler has typehints, an error will be thrown,
similar to the behavior if they would not have the required amount of arguments or were not static.
The restriction that some functions can not declare typehints is not completly new, e.g. constructor must not declare a RETURN typehint,
so I think it is not completly inappropriate to prevent your mentioned problems this way and I will most likely add this to the RFC.

What do others think about this restriction?

Furthermore I added a check that no arguments are passed by reference (like with other magic methods). We can not really prevent that a user manipulates passed objects (as they are always references),
but at least for builtin values (int, string, etc.) it should not possible to modify their source. Also this would lead to problems, as the 1 in something like $a + 1 can not be passed as reference.

Regards,
Jan Böhmer
