Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:102337 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 45262 invoked from network); 21 Jun 2018 09:27:38 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 21 Jun 2018 09:27:38 -0000 Authentication-Results: pb1.pair.com smtp.mail=rudolf.theunissen@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=rtheunissen@php.net; sender-id=unknown Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.217.195 as permitted sender) X-PHP-List-Original-Sender: rudolf.theunissen@gmail.com X-Host-Fingerprint: 209.85.217.195 mail-ua0-f195.google.com Received: from [209.85.217.195] ([209.85.217.195:45807] helo=mail-ua0-f195.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 27/FB-32156-68F6B2B5 for ; Thu, 21 Jun 2018 05:27:35 -0400 Received: by mail-ua0-f195.google.com with SMTP id k14-v6so1577839uao.12 for ; Thu, 21 Jun 2018 02:27:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=MGpEuvxgg43zGtks/1OBNyVk6ndDi/I/t1jVyMawa5w=; b=SXLGA7TTkG5H6Iy3xr4HeoegSMKI30fLxoB1N+eagamc7K/fSLIPPWEJFU3eZw5GDj y6zV9q+ZC9Hoz93+xpi5hs++1g8y+DaGVKq8t1RGGB7NoFZFuOCvDZpHzHs7l815/STA iS81yBuG2Gi2ZCMZCtSCzOn7+mlXsyuiOWnTUKhSyOkrWzLk5e06FGtoFqEydoONcqte rAHSuTiX/Sahr2kO1KW+/T2qviNyhzaH2uOaCfklk7G9bWFNvhmgEh746c/eH32fP7RI YXF20aGkxMSSqH6g19puYQMEZ4k4sb2JHtUUVAAzw19/qfROPD8F0llJR5Ou/uIAn4ew QqiA== X-Gm-Message-State: APt69E1LSoNHvsznXHuSZjfCfzdthNB1lzBrpYMChYURNLnMM9kI7MeY 0Dm4p7KZPup+CBXjpJHuY3vpr+6j9Ms= X-Google-Smtp-Source: ADUXVKKzD8/ch6EpVi9LsZNIQMNCmwLtBRBtTFfRysS1MJKNJXhuZymjeR/yIxOZjolbFPiniye5vQ== X-Received: by 2002:ab0:30f6:: with SMTP id d22-v6mr16557461uam.58.1529573251369; Thu, 21 Jun 2018 02:27:31 -0700 (PDT) Received: from mail-ua0-f175.google.com (mail-ua0-f175.google.com. [209.85.217.175]) by smtp.gmail.com with ESMTPSA id h47-v6sm2827133uaa.13.2018.06.21.02.27.30 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 21 Jun 2018 02:27:30 -0700 (PDT) Received: by mail-ua0-f175.google.com with SMTP id s13-v6so1601592uad.2 for ; Thu, 21 Jun 2018 02:27:30 -0700 (PDT) X-Received: by 2002:ab0:4802:: with SMTP id b2-v6mr15518979uad.36.1529573250506; Thu, 21 Jun 2018 02:27:30 -0700 (PDT) MIME-Version: 1.0 Date: Thu, 21 Jun 2018 05:27:19 -0400 X-Gmail-Original-Message-ID: Message-ID: To: internals@lists.php.net Content-Type: multipart/alternative; boundary="0000000000004127c5056f238610" Subject: Equality and relative ordering of objects From: rtheunissen@php.net (Rudi Theunissen) --0000000000004127c5056f238610 Content-Type: text/plain; charset="UTF-8" The Comparable RFC (http://wiki.php.net/rfc/comparable) was written in 2010 and revised in 2015 by Adam Harvey, but was not conclusive. I would like to take on some shared responsibility to push this forward and re-open the discussion. Why is this useful, and why should it be added to PHP? 1. The default comparison of objects can be expensive. Objects are compared and tested for equality by recursively comparing their properties. If an object has many properties, or properties that are objects or have large arrays, this comparison may dive very deep. It may not be necessary to do all this work to determine whether two objects are equal. For example, if you have a data model that represents a database entity, you're only concerned about the data attributes, and there's no need to compare the database connection or other internal metadata. Objects that have a defining value (like a number type), can simply compare itself in value to the other object (if also a number type), and can skip checking other properties. This has the potential to save a lot of unnecessary comparisons. 2. The default comparison of objects can be risky. There could be cases where two objects have the same public properties but one of them (perhaps a subclass) has a private property. These two objects could be considered equal to the outside world, but `==` would return `false`. When a developer added this private property, it might not have been obvious that they were affecting equality. This makes `==` checks on objects very fragile where it could be explictly defined and controlled. 3. There is no way to specify strict comparison. With PHP becoming more type-safe since PHP 7 with strict mode and scalar type-hints, the current behaviour feels out of date. For example: ``` $a = new stdClass(); $b = new stdClass(); $a->prop = 1; $b->prop = true; $a == $b; // true ``` 4. Adding this functionality does not affect backwards-compatibility and could probably be implemented in 5.x as well. To my knowledge, The only major changes required are in the default object handlers. This would automatically affect functions like `array_search` and `sort`. 5. Other major languages support this and there seems to be a decent amount of public support for it. What is the proposed API for this? Magic methods. Objects that don't implement them maintain the current behaviour. ``` /** * @returns -1 if less than, * 0 if equal to, * 1 if greater than the other value (in terms of relative ordering). */ __compareTo(mixed $other): int; /** * @returns true if this instance is equal to the given value, or false if not. */ __equals(mixed $other): bool; ``` Why magic methods? - No risk of breaking backwards compatibility because method names that start with `__` are always reserved. - All objects can already be compared and tested for equality, so checking if an object implements an interface would not give you any useful information. Alternatively, we could introduce a function like `is_comparable`. - Python uses magic methods because there are no interfaces. Java has a Comparable interface because not all objects are comparable by default. It's important to note that we're not exposing the ability to make an object comparable, but instead to change the default behaviour when compared. All objects are technically already comparable. - PHP already uses magic methods to change the default behaviour of objects. - Interfaces like `Iterable` or `IteratorAggregate` must be interfaces because not all objects are iterable. Why do we need `__equals` when `__compareTo` can just return `0`? 1. Anything can be tested for equality, but all things are not comparable. For example, an apple can be equated with an orange and we can say that the apple does not equal the orange. However, comparing the apple to an orange makes no logical sense. There is nothing about either object that would allow us to say that the apple is greater or less than the orange. 2. Some things that are not equal have the same relative ordering. For example, a floating point value of `2.0` does not equal the integer `2`, but their relative ordering is the same, and a comparison would return `0`. 3. The contexts in which they are called are not the same. Testing for equality occurs when an object is compared with another using `==` or in functions like `array_search`. The question we're asking is whether the two values are considered equal, and we're not concerned with ordering. The context for comparison is when we need to determine the relative ordering of two values using `<`, `>`, or in functions like `sort`. Issues to be discussed: 1. What happens when we use `<=` and `>=`? Does the "or equal to" part call `__equals` or does it check if `__compareTo` returns 0? 2. Should we also expose strict comparison, ie. `===` ? 3. Naming: - `__eq` - `__equalTo` - `__equals` - `__compareTo` - `__comparedTo` - `__compare` - `__cmp` --0000000000004127c5056f238610--