Skip to content

Commit

Permalink
Auto merge of #103046 - JanBeh:PR_clarify_cmp_terminology, r=workingj…
Browse files Browse the repository at this point in the history
…ubilee

docs: Correct terminology in std::cmp

This PR is the result of some discussions on URLO:

* [Traits in `std::cmp` and mathematical terminology](https://users.rust-lang.org/t/traits-in-std-cmp-and-mathematical-terminology/69887)
* [Are poker hands `Ord` or `PartialOrd`?](https://users.rust-lang.org/t/are-poker-hands-ord-or-partialord/82644)

Arguably, the documentation currently isn't very precise regarding mathematical terminology. This can lead to misunderstandings of what `PartialEq`, `Eq`, `PartialOrd`, and `Ord` actually do.

While I believe this PR doesn't give any new API guarantees, it expliclitly mentions that `PartialEq::eq(a, b)` may return `true` for two distinct values `a` and `b` (i.e. where `a` and `b` are not equal in the mathematical sense). This leads to the consequence that `Ord` may describe a weak ordering instead of a total ordering.

In either case, I believe this PR should be thoroughly reviewed, ideally by someone with mathematical background to make sure the terminology is correct now, and also to ensure that no unwanted new API guarantees are made.

In particular, the following problems are addressed:

* Some clarifications regarding used (mathematical) terminology:
    * Avoid using the terms "total equality" and "partial equality" in favor of "equivalence relation" and "partial equivalence relation", which are well-defined and unambiguous.
    * Clarify that `Ordering` is an ordering between two values (and not an order in the mathematical sense).
    * Avoid saying that `PartialEq` and `Eq` are "equality comparisons" because the terminology "equality comparison" could be misleading: it's possible to implement `PartialEq` and `Eq` for other (partial) equivalence relations, in particular for relations where `a == b` for some `a` and `b` even when `a` and `b` are not the same value.
    * Added a section "Strict and non-strict partial orders" to document that the `<=` and `>=` operators do not correspond to non-strict partial orders.
    * Corrected section "Corollaries" in documenation of `Ord` in regard to `<` only describing a strict total order in cases where `==` conforms to mathematical equality.
    * ~~Added a section "Weak orders" to explain that `Ord` may also describe a weak order or total preorder, depending on how `PartialEq::eq` has been implemented.~~ (Removed, see [comment](#103046 (comment)))
* Made documentation easier to understand:
    * Explicitly state at the beginning of `PartialEq`'s documentation comment that implementing the trait will provide the `==` and `!=` operators.
    * Added an easier to understand rule when to implement `Eq` in addition to `PartialEq`: "if it’s guaranteed that `PartialEq::eq(a, a)` always returns `true`."
    *  Explicitly mention in documentation of `Eq` that the properties "symmetric" and "transitive" are already required by `PartialEq`.
  • Loading branch information
bors committed Oct 5, 2023
2 parents cf9fd95 + 86b031b commit 3bcad65
Showing 1 changed file with 50 additions and 17 deletions.
67 changes: 50 additions & 17 deletions library/core/src/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
//! This module contains various tools for comparing and ordering values. In
//! summary:
//!
//! * [`Eq`] and [`PartialEq`] are traits that allow you to define total and
//! partial equality between values, respectively. Implementing them overloads
//! the `==` and `!=` operators.
//! * [`PartialEq<Rhs>`] overloads the `==` and `!=` operators. In cases where
//! `Rhs` (the right hand side's type) is `Self`, this trait corresponds to a
//! partial equivalence relation.
//! * [`Eq`] indicates that the overloaded `==` operator corresponds to an
//! equivalence relation.
//! * [`Ord`] and [`PartialOrd`] are traits that allow you to define total and
//! partial orderings between values, respectively. Implementing them overloads
//! the `<`, `<=`, `>`, and `>=` operators.
//! * [`Ordering`] is an enum returned by the main functions of [`Ord`] and
//! [`PartialOrd`], and describes an ordering.
//! [`PartialOrd`], and describes an ordering of two values (less, equal, or
//! greater).
//! * [`Reverse`] is a struct that allows you to easily reverse an ordering.
//! * [`max`] and [`min`] are functions that build off of [`Ord`] and allow you
//! to find the maximum or minimum of two values.
Expand All @@ -27,16 +30,21 @@ pub(crate) use bytewise::BytewiseEq;

use self::Ordering::*;

/// Trait for equality comparisons.
/// Trait for comparisons using the equality operator.
///
/// Implementing this trait for types provides the `==` and `!=` operators for
/// those types.
///
/// `x.eq(y)` can also be written `x == y`, and `x.ne(y)` can be written `x != y`.
/// We use the easier-to-read infix notation in the remainder of this documentation.
///
/// This trait allows for partial equality, for types that do not have a full
/// equivalence relation. For example, in floating point numbers `NaN != NaN`,
/// so floating point types implement `PartialEq` but not [`trait@Eq`].
/// Formally speaking, when `Rhs == Self`, this trait corresponds to a [partial equivalence
/// relation](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
/// This trait allows for comparisons using the equality operator, for types
/// that do not have a full equivalence relation. For example, in floating point
/// numbers `NaN != NaN`, so floating point types implement `PartialEq` but not
/// [`trait@Eq`]. Formally speaking, when `Rhs == Self`, this trait corresponds
/// to a [partial equivalence relation].
///
/// [partial equivalence relation]: https://en.wikipedia.org/wiki/Partial_equivalence_relation
///
/// Implementations must ensure that `eq` and `ne` are consistent with each other:
///
Expand Down Expand Up @@ -242,15 +250,15 @@ pub macro PartialEq($item:item) {
/* compiler built-in */
}

/// Trait for equality comparisons which are [equivalence relations](
/// Trait for comparisons corresponding to [equivalence relations](
/// https://en.wikipedia.org/wiki/Equivalence_relation).
///
/// This means, that in addition to `a == b` and `a != b` being strict inverses, the equality must
/// be (for all `a`, `b` and `c`):
/// This means, that in addition to `a == b` and `a != b` being strict inverses,
/// the relation must be (for all `a`, `b` and `c`):
///
/// - reflexive: `a == a`;
/// - symmetric: `a == b` implies `b == a`; and
/// - transitive: `a == b` and `b == c` implies `a == c`.
/// - symmetric: `a == b` implies `b == a` (required by `PartialEq` as well); and
/// - transitive: `a == b` and `b == c` implies `a == c` (required by `PartialEq` as well).
///
/// This property cannot be checked by the compiler, and therefore `Eq` implies
/// [`PartialEq`], and has no extra methods.
Expand All @@ -260,6 +268,10 @@ pub macro PartialEq($item:item) {
/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
/// methods.
///
/// Implement `Eq` in addition to `PartialEq` if it's guaranteed that
/// `PartialEq::eq(a, a)` always returns `true` (reflexivity), in addition to
/// the symmetric and transitive properties already required by `PartialEq`.
///
/// ## Derivable
///
/// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
Expand Down Expand Up @@ -676,12 +688,19 @@ impl<T: Clone> Clone for Reverse<T> {
///
/// ## Corollaries
///
/// From the above and the requirements of `PartialOrd`, it follows that `<` defines a strict total order.
/// This means that for all `a`, `b` and `c`:
/// From the above and the requirements of `PartialOrd`, it follows that for
/// all `a`, `b` and `c`:
///
/// - exactly one of `a < b`, `a == b` or `a > b` is true; and
/// - `<` is transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
///
/// Mathematically speaking, the `<` operator defines a strict [weak order]. In
/// cases where `==` conforms to mathematical equality, it also defines a
/// strict [total order].
///
/// [weak order]: https://en.wikipedia.org/wiki/Weak_ordering
/// [total order]: https://en.wikipedia.org/wiki/Total_order
///
/// ## Derivable
///
/// This trait can be used with `#[derive]`.
Expand Down Expand Up @@ -920,6 +939,20 @@ pub macro Ord($item:item) {
/// - transitivity of `>`: if `a > b` and `b > c` then `a > c`
/// - duality of `partial_cmp`: `partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)`
///
/// ## Strict and non-strict partial orders
///
/// The `<` and `>` operators behave according to a *strict* partial order.
/// However, `<=` and `>=` do **not** behave according to a *non-strict*
/// partial order.
/// That is because mathematically, a non-strict partial order would require
/// reflexivity, i.e. `a <= a` would need to be true for every `a`. This isn't
/// always the case for types that implement `PartialOrd`, for example:
///
/// ```
/// let a = f64::sqrt(-1.0);
/// assert_eq!(a <= a, false);
/// ```
///
/// ## Derivable
///
/// This trait can be used with `#[derive]`.
Expand Down

0 comments on commit 3bcad65

Please sign in to comment.