Re: Custom object equality
| From: | Larry Garfield | Date: | Wed, 18 Oct 2023 16:42:53 +0000 |
| Subject: | Re: Custom object equality | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-121408@lists.php.net to get a copy of this message | ||
On Wed, Oct 18, 2023, at 1:12 PM, Robert Landers wrote:
> On Wed, Oct 18, 2023 at 2:51 PM someniatko <someniatko@gmail.com> wrote:
>>
>> Hi internals,
>>
>> There is often a need to compare whether two objects are equal. For
>> example, a popular [brick/money](https://packagist.org/packages/brick/money)
>> library has a
Money class, which has an
>> equals() method. However, this
>> becomes tedious to implement such methods, when multiple nested objects are
>> involved. For instance, Money has an amount and a currency.
>>
>> There were already suggestions on the mailing list to allow "overloading"
>> existing == operator, and some suggestions went even as far
>> as
>> overloading <, >= etc
>> operators. However, overloading existing
>> operators may lead to a BC break between the PHP version which does support
>> the functionality, and which doesn't. It won't result in a BC break in case
>> it's an entirely new syntax, because a library or a project won't be able
>> to write code which overloads == in say PHP 8.4, but with
>> code which
>> still works in PHP 8.3 - it will have to require PHP 8.4+. But if it is
>> implemented as a magic method, then == will work
>> differently for 8.3 and
>> 8.4.
>>
>> I suggest thinking about introducing a new operator ~=,
>> which signifies
>> that custom equality is requested. In such a case, == will
>> work as it
>> works now, and by default ~= will work also like
>> ==, unless its
>> behavior is overwritten via a magic method. If a magic method is not
>> present in a class being compared, ~= will compare two
>> objects field by
>> field, but using ~= comparison rather than
>> == comparison, recursively.
>>
>> For instance, a Money object may consist of Amount and Currency. If two
>> Moneys are compared using ~=, and Money does not implement
>> a magic
>> method, Amount also doesn't implement it, but Currency does, then Amounts
>> are compared using ~= which is equal to
>> == comparison in this case,
>> but Currencies are compared using their custom comparison logic.
>>
>> This approach allows combining
>> - no BC break - ~= is a new syntax which is unavailable in
>> older PHP
>> versions
>> - explicitly showing an intent that objects are compared using a custom
>> comparison, rather than standard PHP one
>> - allow to skip writing boilerplate equals() methods which just forward
>> equals() to the nested objects
>> - standardize such comparisons on the language level
>>
>> Of course how exactly this operator looks may be changed,
>> ~= is just an
>> example.
>>
>> WDYT?
>>
>> Regards,
>> Illia / someniatko
>
> I like it.
>
> One thing I really like about this proposal is that, in theory, it
> could open the door to new operators like ~<, ~<=, ~!, ~= which can be
> similarly implemented.
>
> It also makes me wonder if something similar to scala can be
> implemented, where any operator beginning with "~" can just call an
> associated magic method. I'm not sure how it would be implemented
> though, since I don't think you can use symbols/operators in method
> names. But if you wanted a pipe operator, you could add one via ~|.
> It'd allow for more useful DSL's that are implemented in PHP, as
> currently, it doesn't really make sense to create a DSL that is
> actually PHP since you can't override any operators.
>
> That being said, my only criticism is that it will be really easy to
> forget the ~ when writing code. It's a true shame that overloading
> wasn't added to the language, as it makes it an impractical language
> for entire classes of problems/solutions.
>
> Robert Landers
> Software Engineer
> Utrecht NL
Honestly I'm still on team operator-override. *Most* major languages have operator
overloading, and it doesn't seem to cause a problem. (According to my research, C++, Kotlin,
Swift, Rust, C#, Haskell, Python, and Ruby all have operator overloading. Go, TypeScript, PHP, and
Javascript do not. Java has comparison-only overloading so it's in the middle.) Most of the
"it causes so many problems" arguments are, from what I can tell, greatly
over-exaggerated.
More to the point, the key question is whether anyone who voted No on the previous operator
overloading RFC would vote Yes on an operator-overloading-but-only-with-an-opt-in-prefix RFC. If
not, then it doesn't really buy us anything. If so, then it's possible as a compromise
but would, frankly, be strictly inferior to just using the native operators we already have.
So the real question is: Would anyone who voted No on operator overloading before vote Yes on it if
it used ~=, ~>, ~+, etc. instead?
---Larry Garfield