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_overloadingThe 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 dont 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
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
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_overloadingThe 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öhmerI 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 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]
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).Naming: If we're already going for more verbose names, I'd prefer
__shiftLeft over __lshift.
Yes, please.
-Mike
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: > 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 likeinterface 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
Am 02.03.2020 um 15:30 schrieb Nikita Popov:
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_overloadingThe 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öhmerI 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,
JanSome 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 likeinterface 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,
NikitaI 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
Am 02.03.2020 um 15:30 schrieb Nikita Popov:
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.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?
I agree that we should not introduce (this special case of) overloaded
functions.
Thanks,
Christoph
Am 02.03.2020 um 15:30 schrieb Nikita Popov:
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.
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?
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
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.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]
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.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 =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 TypeErrorIf 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
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
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]