Hi everyone,
This is an RFC that is based on previous discussions here:
https://externals.io/message/102337
RFC: https://wiki.php.net/rfc/object-comparison
The implementation is tested and appears to be working as expected. :)
- Rudi Theunissen
some ideas and concerns:
-
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to core -
would be nice if compareTo and equals were used only if left operand and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals are
two different functions -
otherwise RFC should specify the precedence. if $left operand and $right
operand both have the magic methods, it will call $left->__magic($right),
otherwise, if only the right one has the handler?
what if the right one has compareTo and the left has only equal? you
probably should add a table that explains which method is called depending
in the availability of the two magic methods in the operands. -
I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design. You could do that when "assertions" are active.
gl
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to core
Actually not sure this can be done as an extension because it changes
zend_operators and introduces a new handler to make it all work.
Would be nice if compareTo and equals were used only if left operand and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals are
two different functions
So effectively it means we only call __equals if both sides are instances
of the same class?
If $left operand and $right operand both have the magic methods, it will
call $left->__magic($right), otherwise, if only the right one has the
handler? What if the right one has compareTo and the left has only equal?
you probably should add a table that explains which method is called
depending in the availability of the two magic methods in the operands.
Good idea. :) In brief, as it's implemented right now, the LHS takes
precedence and the RHS will be called if it's the only one that implements
the operation. I'll write some more tests for these cases and build that
table for the RFC.
I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design.
If we want to guarantee symmetry by design, we have to either compare both
sides or only compare if instances of the same class, or document that
comparison should be implemented such that a > b == b < a, etc. I'm not
convinced that a double comparison outweighs the responsibility on the user
to be sensible.
some ideas and concerns:
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to corewould be nice if compareTo and equals were used only if left operand and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals
are two different functionsotherwise RFC should specify the precedence. if $left operand and $right
operand both have the magic methods, it will call $left->__magic($right),
otherwise, if only the right one has the handler?
what if the right one has compareTo and the left has only equal? you
probably should add a table that explains which method is called depending
in the availability of the two magic methods in the operands.I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design. You could do that when "assertions" are active.gl
not some class, only same comparator function. e.g. this is ok, as __equals
is the same for both classes
trait X{ function __equals($o){ ... } }
class A{ use X; }
class B{ use X; }
assert((new A) == (new B));
not some class, only same comparator function. e.g. this is ok, as __equals
is the same for both classes
trait X{ function __equals($o){ ... } }
class A{ use X; }
class B{ use X; }
assert((new A) == (new B));
What does "the same function" mean, though? The same text? The same entry
in memory?
In this example, the Trait will be copied into the class at compile time,
so is not "the same function" in terms of internal representation. It may
not even have the same code, since the magic contant CLASS will be
compiled to 'A' or 'B', never 'X'.
Comparing by text doesn't seem that sensible; you could easily have two
objects with "__compareTo($other) { return $this->value <=> $other-> value
; }" but that doesn't mean they should be comparable.
The only meaningful constraint you could put on it is either "both objects
are of the same class", or assert($other instance self) - i.e. compare
against the same class or a sub-class.
I think it's more straight-froward to always call the handler, and let the
user decide which comparisons are computable.
Regards,
Rowan Collins
[IMSoP]
śr., 27 cze 2018 o 03:29 Rudolf Theunissen rudolf.theunissen@gmail.com
napisał(a):
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to core
It was possible in Sara's extension[1], so maybe now also? Take a look.
Actually not sure this can be done as an extension because it changes
zend_operators and introduces a new handler to make it all work.Would be nice if compareTo and equals were used only if left operand and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals are
two different functionsSo effectively it means we only call __equals if both sides are instances
of the same class?If $left operand and $right operand both have the magic methods, it will
call $left->__magic($right), otherwise, if only the right one has the
handler? What if the right one has compareTo and the left has only equal?
you probably should add a table that explains which method is called
depending in the availability of the two magic methods in the operands.Good idea. :) In brief, as it's implemented right now, the LHS takes
precedence and the RHS will be called if it's the only one that implements
the operation. I'll write some more tests for these cases and build that
table for the RFC.I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design.If we want to guarantee symmetry by design, we have to either compare both
sides or only compare if instances of the same class, or document that
comparison should be implemented such that a > b == b < a, etc. I'm not
convinced that a double comparison outweighs the responsibility on the user
to be sensible.some ideas and concerns:
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to corewould be nice if compareTo and equals were used only if left operand
and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals
are two different functionsotherwise RFC should specify the precedence. if $left operand and
$right
operand both have the magic methods, it will call $left->__magic($right),
otherwise, if only the right one has the handler?
what if the right one has compareTo and the left has only equal? you
probably should add a table that explains which method is called
depending
in the availability of the two magic methods in the operands.I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design. You could do that when "assertions" are active.gl
[1] https://github.com/php/pecl-php-operator/blob/master/operator.c
regards / pozdrawiam,
Michał Brzuchalski
about.me/brzuchal
brzuchalski.com
śr., 27 cze 2018 o 16:55 Michał Brzuchalski michal@brzuchalski.com
napisał(a):
śr., 27 cze 2018 o 03:29 Rudolf Theunissen rudolf.theunissen@gmail.com
napisał(a):I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to coreIt was possible in Sara's extension[1], so maybe now also? Take a look.
Actually not sure this can be done as an extension because it changes
zend_operators and introduces a new handler to make it all work.Would be nice if compareTo and equals were used only if left operand and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals
are
two different functionsSo effectively it means we only call __equals if both sides are instances
of the same class?If $left operand and $right operand both have the magic methods, it will
call $left->__magic($right), otherwise, if only the right one has the
handler? What if the right one has compareTo and the left has only equal?
you probably should add a table that explains which method is called
depending in the availability of the two magic methods in the operands.Good idea. :) In brief, as it's implemented right now, the LHS takes
precedence and the RHS will be called if it's the only one that implements
the operation. I'll write some more tests for these cases and build that
table for the RFC.I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design.If we want to guarantee symmetry by design, we have to either compare both
sides or only compare if instances of the same class, or document that
comparison should be implemented such that a > b == b < a, etc. I'm not
convinced that a double comparison outweighs the responsibility on the
user
to be sensible.some ideas and concerns:
I would like to see this in an extension first, i think it's perfectly
doable and people can test it before merging to corewould be nice if compareTo and equals were used only if left operand
and
right operand used the same comparator function.
In other words, $a == $b must not work if $a->__equals and $b->__equals
are two different functionsotherwise RFC should specify the precedence. if $left operand and
$right
operand both have the magic methods, it will call
$left->__magic($right),
otherwise, if only the right one has the handler?
what if the right one has compareTo and the left has only equal? you
probably should add a table that explains which method is called
depending
in the availability of the two magic methods in the operands.I'd introduce a debug mode that forces php to call both
$left->__equals($right) and $right->__equals($left) so that symmetry is
guaranteed by design. You could do that when "assertions" are active.gl
[1] https://github.com/php/pecl-php-operator/blob/master/operator.c
regards / pozdrawiam,
Michał Brzuchalski
about.me/brzuchal
brzuchalski.com
There is also one more thing I can think of. If __equals and __compareTo
methods
don't have any restrictions for $other parameter typehint then I'll be able
to declare
Foo {
public function __compareTo(int $other): int {}
}
and will get some error on
new Foo() == new DateTime('now');
Right?
--
regards / pozdrawiam,
Michał Brzuchalski
about.me/brzuchal
brzuchalski.com
There is also one more thing I can think of. If __equals and __compareTo
methods
don't have any restrictions for $other parameter typehint then I'll be able
to declare
Foo {
public function __compareTo(int $other): int {}
}
and will get some error on
new Foo() == new DateTime('now');
This can already happen with other magic methods. For instance:
class A {
public function __set(int $foo, int $bar) {}
}
$a = new A;
$a->test = 42;
TypeError: Argument 1 passed to A::__set() must be of the type int,
string given
As long as the implementation correctly unwinds the stack when a Throwable
is encountered (__toString can't cope with this, and bails out the
engine), I don't think this is a problem.
Regards,
Rowan Collins
[IMSoP]
If $left operand and $right operand both have the magic methods, it will
call $left->__magic($right), otherwise, if only the right one has the
handler? What if the right one has compareTo and the left has only equal?
you probably should add a table that explains which method is called
depending in the availability of the two magic methods in the operands.I think $left == $right => $left->__equals($right) makes perfect sense.
$left dictates fallbacks, since $left is the one invoking the method. If
$left doesn't have __equals, then it uses __compareTo... if it doesn't have
either, it falls back to the original way. Bottom line, the existence or
non-existence of either method on the right side doesn't matter. As with
all type juggling, you run the risk that ($a == $b) !== ($b == $a). The
other option would just be to force both objects to define at least the
__compareTo method, and if one of them doesn't, it falls back to the old
way. I woudn't go as far as requiring they be the same functions though...
existence should be good enough. Maybe throw a warning if used for classes
of different types or where the methods are different - but if I have class
Apple and class Orange, and want to compare them, I should be able to do so!
Has implementing this using an interface? Just like ArrayAccess defines
methods that allow object to be referenced like an array, Comparable could
provide methods (equals and comparesTo) that would override the comparison
operators. Personally, I like magic methods, so I'm good with the RFC as-is.
--
-- Chase
chasepeeler@gmail.com
Hi!
In other words, $a == $b must not work if $a->__equals and $b->__equals are
two different functionsSo effectively it means we only call __equals if both sides are instances
of the same class?
We can just define that == calls __equals on the left. That's, for
example, what Python does. This would produce a weird consequence of $a
== $b and $b == $a not being the same, but that's what you get for
overloading operators. That's why overloading operators is not always
the best idea, because of disconnect between perceived semantics (math
equality operator, which is symmetric, reflexive and transitive) and
actual semantic (which is not guaranteed to have any of the math
equality properties).
In any case, "the same class" would be too restrictive, as there's
inheritance, so we should support LSP. But then again, if $a and $b are
in different places on inheritance hierarchy, we can can non-symmetric
operator. This is kinda deep problem that is not really working well in
most Java code, for example. See:
https://www.artima.com/pins1ed/object-equality.html (for reference,
Martin Odersky is the guy who did Java generics, and designed Scala, so
he knows a quite bit about OO things :)
--
Stas Malyshev
smalyshev@gmail.com
On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
rudolf.theunissen@gmail.com wrote:
Hi everyone,
This is an RFC that is based on previous discussions here:
https://externals.io/message/102337RFC: https://wiki.php.net/rfc/object-comparison
The implementation is tested and appears to be working as expected. :)
I had some off-list contact with Rudi and generally have agreed with
these changes.
However, there may be some value in following Python's lead. In Python
2 they had a __cmp__
magic method and in Python 3 they removed it
and added all of these:
lt
le
eq
ne
gt
ge
This permits things such as NumPy to overload < to do an element-wise
comparison of the members; something like this:
np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6])
// evaluates to np.array([true, false, true, false)
If we pursue this Pythonic route then we would also need methods for +
-
- / % etc. I do not want to derail the conversation too much, but it
does seem opportune to discuss operator overloading more generally.
- / % etc. I do not want to derail the conversation too much, but it
On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
rudolf.theunissen@gmail.com wrote:Hi everyone,
This is an RFC that is based on previous discussions here:
https://externals.io/message/102337RFC: https://wiki.php.net/rfc/object-comparison
The implementation is tested and appears to be working as expected. :)
I had some off-list contact with Rudi and generally have agreed with
these changes.However, there may be some value in following Python's lead. In Python
2 they had a__cmp__
magic method and in Python 3 they removed it
and added all of these:lt
le
eq
ne
gt
geThis permits things such as NumPy to overload < to do an element-wise
comparison of the members; something like this:np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6]) // evaluates to np.array([true, false, true, false)
If we pursue this Pythonic route then we would also need methods for +
- / % etc. I do not want to derail the conversation too much, but it
does seem opportune to discuss operator overloading more generally.
We would still need __compareTo
if we did operator overloading
because we have the <=>
operator. Rudi has pointed out that because
of this we should be able to overload individual operators at a later
point without backwards compatibility issues if we so choose. I think
this is true if we do not constrain the return types.
In summary, I propose that return type constraints should be removed
which will allow us to pursue specific operator overloading at a later
point if we desire; an author can always add : bool
or : int
to
their own methods if they so desire.
In summary, I propose that return type constraints should be removed
which will allow us to pursue specific operator overloading at a later
point if we desire
There are no return types or parameter types. The only requirement is that
the function is public and not static.
On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
rudolf.theunissen@gmail.com wrote:Hi everyone,
This is an RFC that is based on previous discussions here:
https://externals.io/message/102337RFC: https://wiki.php.net/rfc/object-comparison
The implementation is tested and appears to be working as expected. :)
I had some off-list contact with Rudi and generally have agreed with
these changes.However, there may be some value in following Python's lead. In Python
2 they had a__cmp__
magic method and in Python 3 they removed it
and added all of these:lt
le
eq
ne
gt
geThis permits things such as NumPy to overload < to do an element-wise
comparison of the members; something like this:np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6]) // evaluates to np.array([true, false, true, false)
If we pursue this Pythonic route then we would also need methods for +
- / % etc. I do not want to derail the conversation too much, but it
does seem opportune to discuss operator overloading more generally.We would still need
__compareTo
if we did operator overloading
because we have the<=>
operator. Rudi has pointed out that because
of this we should be able to overload individual operators at a later
point without backwards compatibility issues if we so choose. I think
this is true if we do not constrain the return types.In summary, I propose that return type constraints should be removed
which will allow us to pursue specific operator overloading at a later
point if we desire; an author can always add: bool
or: int
to
their own methods if they so desire.
Hi everyone,
I've made some changes to the RFC and the implementation today to explain
some of the edge cases and combined behaviour better.
There are some issues towards the bottom of the RFC that I would really
like some opinions on. They all have decisions made but are not concrete,
so I'd like to know if anyone has strong opinions on them before they are
considered a final part of the RFC. I'm happy with the current state but
I'm very open to changing those decisions if there's a good enough reason
to.
See https://wiki.php.net/rfc/object-comparison#open_issues
Thank you
On Wed, 27 Jun 2018 at 18:53, Rudolf Theunissen rudolf.theunissen@gmail.com
wrote:
In summary, I propose that return type constraints should be removed
which will allow us to pursue specific operator overloading at a later
point if we desireThere are no return types or parameter types. The only requirement is that
the function is public and not static.On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
rudolf.theunissen@gmail.com wrote:Hi everyone,
This is an RFC that is based on previous discussions here:
https://externals.io/message/102337RFC: https://wiki.php.net/rfc/object-comparison
The implementation is tested and appears to be working as expected. :)
I had some off-list contact with Rudi and generally have agreed with
these changes.However, there may be some value in following Python's lead. In Python
2 they had a__cmp__
magic method and in Python 3 they removed it
and added all of these:lt
le
eq
ne
gt
geThis permits things such as NumPy to overload < to do an element-wise
comparison of the members; something like this:np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6]) // evaluates to np.array([true, false, true, false)
If we pursue this Pythonic route then we would also need methods for +
- / % etc. I do not want to derail the conversation too much, but it
does seem opportune to discuss operator overloading more generally.We would still need
__compareTo
if we did operator overloading
because we have the<=>
operator. Rudi has pointed out that because
of this we should be able to overload individual operators at a later
point without backwards compatibility issues if we so choose. I think
this is true if we do not constrain the return types.In summary, I propose that return type constraints should be removed
which will allow us to pursue specific operator overloading at a later
point if we desire; an author can always add: bool
or: int
to
their own methods if they so desire.
Hi everyone,
I've made some changes to the RFC and the implementation today to explain
some of the edge cases and combined behaviour better.There are some issues towards the bottom of the RFC that I would really
like some opinions on. They all have decisions made but are not concrete,
so I'd like to know if anyone has strong opinions on them before they are
considered a final part of the RFC. I'm happy with the current state but
I'm very open to changing those decisions if there's a good enough reason
to.See https://wiki.php.net/rfc/object-comparison#open_issues
Thank you
Overall, I like.
Regarding what is legal to compare to, it seems useful to leverage the
optional type hint. So given:
class Foo {
public function __compareTo(Foo $other) {}
}
class Bar extends Foo {
public function __compareTo(Bar $other) {}
}
In this case, the order of checking to see what method to call will also check
for type compatibility. That is, $f < $b checks Foo::__compareTo(), finds it
doesn't match, and so calls Bar::__compareTo() instead, which does.
"However, because magic methods aren't inherited" - Wait, they're not? Are
you certain of that? Because 3v4l.org says they are:
I don't understand Future Scope point 1. It... means we're already covering
those operators. Why is there future scope needed?
I otherwise agree with the current Open Issues directions.
--Larry Garfield
> This permits things such as NumPy to overload < to do an element-wise
> comparison of the members; something like this:
>
> np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6])
> // evaluates to np.array([true, false, true, false)
>
In my opinion, this is exactly the kind of messy operator overloading we
should avoid, but if we DO want it, then its purpose is to over-ride the
syntax, not the meaning, of the operator; so "overloaded <=>" would NOT be
the same as "overloaded comparison".
I think it's fairly crucial that this proposal is NOT just about
overloading operators, it's about overloading *behaviour* which occurs
throughout the language, and as such I think this statement from the RFC is
incorrect:
> __compareTo is equivalent to <=> and __equals is equivalent to ==
If someone implements __compareTo() just to give some domain-specific
meaning to the <=> operator, what will happen when someone passes those
objects to `sort()`? If someone decides they want to return an object from
__equals(), how will switch statements behave? And so on...
Because of that, I would much rather these functions did enforce a return
type. Note that __toString() generates an error for an incorrect return
type when used, and __sleep() raises a Notice and serializes Null instead
of the object. I think __compareTo() and __equals() could act more like a
return type hint, and either throw a TypeError straight away, or attempt to
coerce to the correct type and throw a TypeError on failure.
Then, if we want the ability to override the <=> operator specifically,
that can be added later, and users can return whatever type they want
without breaking all the other places where __compareTo() is called.
Regards,
--
Rowan Collins
[IMSoP]
overloading operators, it's about overloading *behaviour*
That is what this RFC is trying to achieve. Equivalent in behaviour, so
we're providing a way for a class to define how it compares or equates to
another value. We're not providing a way to override the meaning or
function of the comparison operators, and it's up to the implementor to
follow this advice. There's nothing stopping someone making `>` modify the
object. But that's the nature of user-defined behaviour of operators:
giving a developer more flexibility also increases the amount of weird
stuff they can do. The question is simply whether the value of the
flexibility is more than the cost of the potential for misuse.
We're proposing the ability for a user to determine how a class should be
compared to another value. The ability to change the semantics of the
comparison operators is an unavoidable side effect.
> If someone implements __compareTo() just to give some domain-specific
meaning to the <=> operator
That would be bad implementation on their part, and we should mention in
the documentation update that this is not the intended application.
> I think __compareTo() and __equals() could act more like a return type
hint, and either throw a TypeError straight away, or attempt to coerce to
the correct type and throw a TypeError on failure.
Currently they do attempt to convert to boolean and integer, respectively,
but that doesn't achieve much because you can still return nothing in
either case and the coercion would be 0 and false.
--
The decision whether to accept this will come down to the trade-off between
flexibility and potential for misuse. We're giving the developer more power
and responsibility by adding to the capability of the language, but that
can be said for any language-level change that has the potential for
misuse. I would personally opt for more flexibility, with clear direction
on the intended use, and trust that those who decide to leverage it know
what they're doing.
>
>
>> This permits things such as NumPy to overload < to do an element-wise
>> comparison of the members; something like this:
>>
>> np.array([1, 3, 5, 7]) < np.array([2, 1, 6, 6])
>> // evaluates to np.array([true, false, true, false)
>>
>
>
> In my opinion, this is exactly the kind of messy operator overloading we
> should avoid, but if we DO want it, then its purpose is to over-ride the
> syntax, not the meaning, of the operator; so "overloaded <=>" would NOT be
> the same as "overloaded comparison".
>
> I think it's fairly crucial that this proposal is NOT just about
> overloading operators, it's about overloading *behaviour* which occurs
> throughout the language, and as such I think this statement from the RFC is
> incorrect:
>
> > __compareTo is equivalent to <=> and __equals is equivalent to ==
>
> If someone implements __compareTo() just to give some domain-specific
> meaning to the <=> operator, what will happen when someone passes those
> objects to `sort()`? If someone decides they want to return an object from
> __equals(), how will switch statements behave? And so on...
>
> Because of that, I would much rather these functions did enforce a return
> type. Note that __toString() generates an error for an incorrect return
> type when used, and __sleep() raises a Notice and serializes Null instead
> of the object. I think __compareTo() and __equals() could act more like a
> return type hint, and either throw a TypeError straight away, or attempt to
> coerce to the correct type and throw a TypeError on failure.
>
> Then, if we want the ability to override the <=> operator specifically,
> that can be added later, and users can return whatever type they want
> without breaking all the other places where __compareTo() is called.
>
> Regards,
> --
> Rowan Collins
> [IMSoP]
We're proposing the ability for a user to determine how a class should
be
compared to another value. The ability to change the semantics of the
comparison operators is an unavoidable side effect.
Absolutely, I agree with the intent of this RFC. I think what I was suggesting is that if people in future want to overload the operators specifically, we would want to provide an overload for <=> that was separate from __compareTo, so that you could overload that without breaking things like sorting.
I think __compareTo() and __equals() could act more like a return
type
hint, and either throw a TypeError straight away, or attempt to coerce
to
the correct type and throw a TypeError on failure.Currently they do attempt to convert to boolean and integer,
respectively,
but that doesn't achieve much because you can still return nothing in
either case and the coercion would be 0 and false.
Apologies if this is covered in the RFC, but does that apply wherever they are used? For instance, what will the following code do?
class SpaceStation {
public $shape;
public function __construct($shape='[]') {
$this->shape = $shape;
}
public function __compareTo($other) {
return new self($this->shape . '>=<' . $other->shape);
}
}
$a = new SpaceStation;
$b = new SpaceStation;
$c = $a <=> $b; // Error? 0?
echo $c->shape; // or will this echo '[]>=<[]'?
$d = $a < $b; // Same result?
$list = [$a, $b, $c, $d];
sort($list); // Or will the error only be raised here?
The decision whether to accept this will come down to the trade-off
between
flexibility and potential for misuse.
Absolutely, and for what it's worth (which isn't much) I'm +1 on this, if it's not documented as operator overloading (which I think is a separate feature) and can't be used to produce values outside the expected type (due to coercion or errors).
Regards,
--
Rowan Collins
[IMSoP]
I think what I was suggesting is that if people in future want to overload
the operators specifically, we would want to provide an overload for <=>
that was separate from __compareTo, so that you could overload that without
breaking things like sorting.
I think it would be confusing to have both, or to have <=> not be aligned
with __compareTo, because they are semantically equivalent. By overloading
<=>, you have to also override comparison. Separating those is a recipe for
confusion.
Apologies if this is covered in the RFC, but does that apply wherever they
are used? For instance, what will the following code do?
class SpaceStation {
public $shape;
public function __construct($shape='[]') {
$this->shape = $shape;
}
public function __compareTo($other) {
return new self($this->shape . '>=<' . $other->shape);
}
}
$a = new SpaceStation;
$b = new SpaceStation;
$c = $a <=> $b; // Error? 0?
echo $c->shape; // or will this echo '[]>=<[]'?
$d = $a < $b; // Same result?
$list = [$a, $b, $c, $d];
sort($list); // Or will the error only be raised here?
When you use the <=>
operator, the return value is normalised to either
-1, 0 or 1 which is consistent with the current behaviour. So the object
you're returning in compareTo
will be converted to an integer(1) which
will make $c
be 1
. Attempting to call echo $c->shape
then makes no
sense.
$a < $b
will be false because according to your return of 1
, $a
would
be greater.
Sorting would then consider the LHS to always be greater also.
One thing that your example brings up is that <=>
is not exactly
equivalent to calling __compareTo
directly, which would just return the
raw value. This is exciting because it's a new issue that I'll add to the
RFC for consideration. Ideally we would like the operator and the magic
method to behave exactly the same, but that doesn't seem like a viable
option because we wouldn't want to magically translate the return value of
a function you're calling directly. The only option then is to simply
advise that <=> will normalise the return value of __compareTo
. I'm
personally okay with that behaviour if it means the convention of the
return value of <=> remains intact.
I think what I was suggesting is that if people in future want to
overload
the operators specifically, we would want to provide an overload for
<=>
that was separate from __compareTo, so that you could overload that
without
breaking things like sorting.I think it would be confusing to have both, or to have <=> not be
aligned
with __compareTo, because they are semantically equivalent. By
overloading
<=>, you have to also override comparison. Separating those is a recipe
for
confusion.
That's the point though: with operator overloading as defined in many languages, they're only semantically equivalent if the person overloading the operator wants them to be. The most well-known example is probably the C++ operator >> which for primitives is a bit shift, but is overloaded for input and output to streams.
If, on the other hand, operator overloading was added but constrained by the language to certain return values, there would be very little to be gained by overloading each comparison separately - all the extra overloads would have to return boolean anyway.
When you use the
<=>
operator, the return value is normalised to
either
-1, 0 or 1 which is consistent with the current behaviour. So the
object
you're returning incompareTo
will be converted to an integer(1)
That makes perfect sense to me, thanks. Just to reiterate, it's not what fans of free-form operator overloading would want, but I think it's the right behaviour for this feature.
One thing that your example brings up is that
<=>
is not exactly
equivalent to calling__compareTo
directly, which would just return
the
raw value.
Yeah, that's a good point. Directly calling magic methods is always a bit peculiar, but I think a note in the manual is probably enough.
Regards,
--
Rowan Collins
[IMSoP]