Welcome! Log In Create A New Profile

Advanced

[PHP-DEV] [RFC] User-defined object comparison

Posted by Rudolf Theunissen 
Rudolf Theunissen
[PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 02:00AM
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
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 03:40AM
> 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.

On Tue, 26 Jun 2018 at 21:04, Wes <[email protected]> wrote:

> 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
>
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));
Michał Brzuchalski
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 05:00PM
śr., 27 cze 2018 o 03:29 Rudolf Theunissen <[email protected]>
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 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.
>
> On Tue, 26 Jun 2018 at 21:04, Wes <[email protected]> wrote:
>
> > 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
> >
>

[1] https://github.com/php/pecl-php-operator/blob/master/operator.c
--
regards / pozdrawiam,
--
Michał Brzuchalski
about.me/brzuchal
brzuchalski.com
Rowan Collins
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 05:30PM
On 27 June 2018 at 14:30, Wes <[email protected]> wrote:

> 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]
Michał Brzuchalski
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 05:30PM
śr., 27 cze 2018 o 16:55 Michał Brzuchalski <[email protected]>
napisał(a):

>
>
> śr., 27 cze 2018 o 03:29 Rudolf Theunissen <[email protected]>
> 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 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.
>>
>> On Tue, 26 Jun 2018 at 21:04, Wes <[email protected]> wrote:
>>
>> > 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
>> >
>>
>
> [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
Rowan Collins
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 05:50PM
On 27 June 2018 at 16:24, Michał Brzuchalski <[email protected]> wrote:

> 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]
Chase Peeler
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 06:20PM
>
>
> > 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
Levi Morrison
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 08:30PM
On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
<[email protected]> wrote:
>
> 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. :)

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.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Levi Morrison
Re: [PHP-DEV] [RFC] User-defined object comparison
June 27, 2018 09:30PM
On Wed, Jun 27, 2018 at 12:24 PM Levi Morrison <[email protected]> wrote:
>
> On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
> <[email protected]> wrote:
> >
> > 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. :)
>
> 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.

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.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
June 28, 2018 01:00AM
>
> 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 Wed, 27 Jun 2018 at 15:21, Levi Morrison <[email protected]> wrote:

> On Wed, Jun 27, 2018 at 12:24 PM Levi Morrison <[email protected]> wrote:
> >
> > On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
> > <[email protected]> wrote:
> > >
> > > 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. :)
> >
> > 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.
>
> 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.
>
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
June 28, 2018 01:00AM
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 <[email protected]>
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 desire
>
>
> There are no return types or parameter types. The only requirement is that
> the function is public and not static.
>
>
> On Wed, 27 Jun 2018 at 15:21, Levi Morrison <[email protected]> wrote:
>
>> On Wed, Jun 27, 2018 at 12:24 PM Levi Morrison <[email protected]> wrote:
>> >
>> > On Tue, Jun 26, 2018 at 5:50 PM Rudolf Theunissen
>> > <[email protected]> wrote:
>> > >
>> > > 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. :)
>> >
>> > 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.
>>
>> 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.
>>
>
Larry Garfield
Re: [PHP-DEV] [RFC] User-defined object comparison
June 28, 2018 11:20PM
On Wednesday, June 27, 2018 5:58:29 PM CDT Rudolf Theunissen wrote:
> 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:

https://3v4l.org/P6Hlq

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
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
June 29, 2018 04:50AM
>
> 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.


I'm not sure if I like this, it's a bit non-deterministic isn't it.. that
the comparison function
called can be determined by the value being compared? While I'm not
disregarding this
idea entirely, it's another addition we can add at a later stage.

It would be a bit of a juggle internally, to hold on to the type error
while we attempt the
second call, and if that fails, we throw the type error of the first
attempt?

>
> "However, because magic methods aren't inherited" - Wait, they're not? Are
> you certain of that? Because 3v4l.org says they are:
> https://3v4l.org/P6Hlq


Yeah my bad, that was very poorly written, and I'll update it immediately.
What I meant
to say was that they aren't inherited from any base object class like
`equals` is in Java.
So if you just have a base class, magic methods aren't inherited from some
language-level
base class, so you can use whatever parameter and return types you like.

I don't understand Future Scope point 1. It... means we're already covering
> those operators. Why is there future scope needed?


Python, for example, has the ability to override specific comparison
operators, such as

> <[email protected]>
>
__lt__ for <. Point 1 is trying to say that *if* we want to introduce full,
specific operator
overloading in the future, we would already have the magic methods for ==
and <=>.
Rowan Collins
Re: [PHP-DEV] [RFC] User-defined object comparison
June 29, 2018 07:40PM
On 27 June 2018 at 19:24, Levi Morrison <[email protected]> wrote:

> 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]
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
June 30, 2018 08:10PM
> I think it's fairly crucial that this proposal is NOT just about
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.


On Fri, 29 Jun 2018 at 13:35, Rowan Collins <[email protected]> wrote:

> On 27 June 2018 at 19:24, Levi Morrison <[email protected]> wrote:
>
>> 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]
>
Rowan Collins
Re: [PHP-DEV] [RFC] User-defined object comparison
July 01, 2018 01:10AM
On 30 June 2018 19:04:27 BST, Rudolf Theunissen <[email protected]> wrote:
>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]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Larry Garfield
Re: [PHP-DEV] [RFC] User-defined object comparison
July 01, 2018 03:40AM
On Thursday, June 28, 2018 9:47:56 PM CDT Rudolf Theunissen wrote:
> > 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.
>
> I'm not sure if I like this, it's a bit non-deterministic isn't it.. that
> the comparison function
> called can be determined by the value being compared? While I'm not
> disregarding this
> idea entirely, it's another addition we can add at a later stage.
>
> It would be a bit of a juggle internally, to hold on to the type error
> while we attempt the
> second call, and if that fails, we throw the type error of the first
> attempt?

Thinking on this more, I think we have to do it in some fashion. The current
rules in the RFC say:

* Check if $a has a __compareTo() method. If so, use it. Else:
* Check if $b has a __compareTo() method. If so, use it. Else:
* Default behavior.

However, that ignores the case where $a has a __compareTo() that is not type-
compatible with $b. In that case, the current rules in the RFC would imply
"call $a with a type mismatch, get a TypeError", regardless of whether
$b::__compareTo() would work."

I would suggest the better logic be:

if (either $a or $b have a __compareTo()) {
if ($a->__compareTo() exists and is type compatible with $b) {
use $a->:__compareTo()
}
else if ($b->__compareTo() exists and is type compatible with $a) {
use $b->:__compareTo()
}
else {
throw a new InvalidComparisonError (new type, extends TypeError)
}
}
else {
Default behavior
}

That way we don't need to track A's or B's type error, we just say "if either
of these objects is opting in, do whatever we can. If not, it means we don't
want default behavior." Obviously an untyped compareTo() method is always
type compatible with anything, so developers can use that as they desire.


> I don't understand Future Scope point 1. It... means we're already covering
>
> > those operators. Why is there future scope needed?
>
> Python, for example, has the ability to override specific comparison
> operators, such as
>
> > <[email protected]>
>
> __lt__ for <. Point 1 is trying to say that *if* we want to introduce full,
> specific operator
> overloading in the future, we would already have the magic methods for ==
> and <=>.


Oh, so it's just saying that implementing __lessThan() in the future is not
blocked, as that would simply get checked before __compareTo() while
__compareTo() would be the first thing called for <=>. Gotcha. I'm good with
that.

--Larry Garfield
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
July 01, 2018 08:50PM
>
> 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.
Rowan Collins
Re: [PHP-DEV] [RFC] User-defined object comparison
July 01, 2018 11:10PM
On 1 July 2018 19:45:03 BST, Rudolf Theunissen <[email protected]> wrote:
>>
>> 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 in `compareTo` 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]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Stanislav Malyshev
Re: [PHP-DEV] [RFC] User-defined object comparison
July 03, 2018 10:20PM
Hi!

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

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
July 05, 2018 04:30PM
>
> 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).


Invoking on the left would break the symmetry of $a == 1 and 1 == $a. The
current implementation considers both operands but LHS takes precedence. I
think that this is the best approach so that the order of the operands
should not affect the result. Of course, when comparing two objects, that
would only work if their definition of equality is consistent. Honestly,
unless it's an implementation error on the user's part, I don't see this
being a real problem that would cause confusion or require workarounds.

I know this is talked about in detail in the RFC but it's worth repeating
here: the intention of this RFC is to preserve the semantics of the
operators. Operator overloading is an unavoidable side-effect of overriding
the behaviour that the operators are semantically attached to.

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


That was a great read. A lot of these problems seem to stem from method
overloading and hashCode implementation - neither of which are problems
that we need to consider here. The bottom line is that there will be the
potential for asymmetry, I don't think we can avoid that without a lot of
restriction or double-comparison.

Somewhat related, and in response to Levi's mention of it in this thread,
I've added a frequently asked question regarding Python 3's removal of
__cmp__. Please see:
https://wiki.php.net/rfc/object-comparison#why_did_python_3_remove_cmp_in_favour_of_rich_comparisons

....
Christoph M. Becker
Re: [PHP-DEV] [RFC] User-defined object comparison
July 05, 2018 05:40PM
On 05.07.2018 at 16:27, Rudolf Theunissen wrote:

> Somewhat related, and in response to Levi's mention of it in this thread,
> I've added a frequently asked question regarding Python 3's removal of
> __cmp__. Please see:
> https://wiki.php.net/rfc/object-comparison#why_did_python_3_remove_cmp_in_favour_of_rich_comparisons

This section contains a link labelled “PHP 207” which should be “PEP
207”. :)

--
Christoph M. Becker

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Rudolf Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
July 05, 2018 05:50PM
Fixed! Thanks Christoph, I guess I've been typing "PHP" too much lately. :p

On Thu, 5 Jul 2018 at 11:30, Christoph M. Becker <[email protected]> wrote:

> On 05.07.2018 at 16:27, Rudolf Theunissen wrote:
>
> > Somewhat related, and in response to Levi's mention of it in this thread,
> > I've added a frequently asked question regarding Python 3's removal of
> > __cmp__. Please see:
> >
> https://wiki.php.net/rfc/object-comparison#why_did_python_3_remove_cmp_in_favour_of_rich_comparisons
>
> This section contains a link labelled “PHP 207” which should be “PEP
> 207”. :)
>
> --
> Christoph M. Becker
>
Rudi Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
July 09, 2018 01:00AM
Hi everyone,

This RFC is now final; no decision will be changed unless there is a very
good reason to do so. I'm happy with the approach in detail and available
to address any further concerns.

I'm waiting on the sidelines of the potential changes to the release
schedule, but would like to ask whether anyone thinks a vote on this RFC
should be postponed until everyone has had a good chance to consider it? I
don't want to rush this for 7.3 but if we do have more time (or can reach a
consensus one way or another), I personally believe that this is votable in
its current state.

Rudi
Rudi Theunissen
Re: [PHP-DEV] [RFC] User-defined object comparison
July 09, 2018 01:40AM
I'm sorry that this email created a new thread on externals.io - not sure
how to avoid that.

On Sun, 8 Jul 2018 at 18:51, Rudi Theunissen <[email protected]> wrote:

> Hi everyone,
>
> This RFC is now final; no decision will be changed unless there is a very
> good reason to do so. I'm happy with the approach in detail and available
> to address any further concerns.
>
> I'm waiting on the sidelines of the potential changes to the release
> schedule, but would like to ask whether anyone thinks a vote on this RFC
> should be postponed until everyone has had a good chance to consider it? I
> don't want to rush this for 7.3 but if we do have more time (or can reach a
> consensus one way or another), I personally believe that this is votable in
> its current state.
>
> Rudi
>
Sorry, only registered users may post in this forum.

Click here to login