The Comparable
concept defines equality and inequality.
Intuitively, Comparable
objects must define a binary predicate named equal
that returns whether both objects represent the same abstract value. In other words, equal
must check for deep equality. Since "representing the same abstract value" is difficult to express formally, the exact meaning of equality is partially left to interpretation by the programmer with the following guidelines:
equal
value.4/8
should be equal
to an object representing a fraction as 2/4
, because they both represent the mathematical object 1/2
.Moreover, equal
must exhibit properties that make it intuitive to use for determining the equivalence of objects, which is formalized by the laws for Comparable
.
equal
equal
is defined, not_equal
is implemented by default as its complement. For all objects x
, y
of a Comparable
tag, equal
must define an equivalence relation, and not_equal
must be its complement. In other words, for all objects a
, b
, c
of a Comparable
tag, the following must be true (where x == y
and x != y
denote equal(x, y)
and not_equal(x, y)
, respectively):
hana::integral_constant
, hana::map
, hana::optional
, hana::pair
, hana::range
, hana::set
, hana::string
, hana::tuple
, hana::type
EqualityComparable
data types Two data types T
and U
that model the cross-type EqualityComparable concept presented in N3351 automatically model the Comparable
concept by setting
Note that this also makes EqualityComparable types in the usual sense models of Comparable
in the same way.
Let A
and B
be two Comparable
tags. A function \(f : A \to B\) is said to be equality-preserving if it preserves the structure of the Comparable
concept, which can be rigorously stated as follows. For all objects x
, y
of tag A
,
Equivalently, we simply require that f
is a function in the usual mathematical sense. Another property is injectivity, which can be viewed as being a "lossless" mapping. This property can be stated as
This is equivalent to saying that f
maps distinct elements to distinct elements, hence the "lossless" analogy. In other words, f
will not collapse distinct elements from its domain into a single element in its image, thus losing information.
These functions are very important, especially equality-preserving ones, because they allow us to reason simply about programs. Also note that the property of being equality-preserving is taken for granted in mathematics because it is part of the definition of a function. We feel it is important to make the distinction here because programming has evolved differently and as a result programmers are used to work with functions that do not preserve equality.
The equal
and not_equal
methods are "overloaded" to handle distinct tags with certain properties. Specifically, they are defined for distinct tags A
and B
such that
A
and B
share a common tag C
, as determined by the common
metafunctionA
, B
and C
are all Comparable
when taken individuallyis_embedding
metafunction.The method definitions for tags satisfying the above properties are
equal
In the context of programming with heterogeneous values, it is useful to have unrelated objects compare false
instead of triggering an error. For this reason, equal
adopts a special behavior for unrelated objects of tags T
and U
that do not satisfy the above requirements for the cross-type overloads. Specifically, when T
and U
are unrelated (i.e. T
can't be converted to U
and vice-versa), comparing objects with those tags yields a compile-time false value. This has the effect that unrelated objects like float
and std::string
will compare false, while comparing related objects that can not be safely embedded into the same super structure (like long long
and float
because of the precision loss) will trigger a compile-time assertion. Also note that for any tag T
for which the minimal complete definition of Comparable
is not provided, a compile-time assertion will also be triggered because T
and T
trivially share the common tag T
, which is the expected behavior. This design choice aims to provide more flexibility for comparing objects, while still rejecting usage patterns that are most likely programming errors.
Variables | |
constexpr auto | boost::hana::comparing |
Returns a function performing equal after applying a transformation to both arguments.comparing creates an equivalence relation based on the result of applying a function to some objects, which is especially useful in conjunction with algorithms that accept a custom predicate that must represent an equivalence relation. More... | |
constexpr auto | boost::hana::equal |
Returns a Logical representing whether x is equal to y .The equal function can be called in two different ways. First, it can be called like a normal function: More... | |
constexpr auto | boost::hana::not_equal |
Returns a Logical representing whether x is not equal to y .The not_equal function can be called in two different ways. First, it can be called like a normal function: More... | |
constexpr auto boost::hana::comparing |
#include <boost/hana/fwd/comparing.hpp>
Returns a function performing equal
after applying a transformation to both arguments.comparing
creates an equivalence relation based on the result of applying a function to some objects, which is especially useful in conjunction with algorithms that accept a custom predicate that must represent an equivalence relation.
Specifically, comparing
is such that
or, equivalently,
Comparable
concept.Given a Logical Bool
and a Comparable B
, the signature is \( \mathtt{comparing} : (A \to B) \to (A \times A \to Bool) \).
constexpr auto boost::hana::equal |
#include <boost/hana/fwd/equal.hpp>
Returns a Logical
representing whether x
is equal to y
.The equal
function can be called in two different ways. First, it can be called like a normal function:
However, it may also be partially applied to an argument by using equal.to
:
In other words, equal.to(x)
is a function object that is equivalent to partial(equal, x)
. This is provided to enhance the readability of some constructs, especially when using higher order algorithms.
Given a Logical Bool
and two Comparables A
and B
that share a common embedding, the signature is \( \mathtt{equal} : A \times B \to Bool \).
x,y | Two objects to compare for equality. |
Rationale for the arity of
equal
It is a valid question whether
equal
should accept more than 2 arguments and have semantics matching those of Python's==
. This is not supported right now for the following reasons:
- It was implemented in the MPL11, but it was not shown to be useful so far.
- It does not make sense for
not_equal
to have an arity of more than 2, onlyequal
could maybe have those semantics, which would break symmetry.
constexpr auto boost::hana::not_equal |
#include <boost/hana/fwd/not_equal.hpp>
Returns a Logical
representing whether x
is not equal to y
.The not_equal
function can be called in two different ways. First, it can be called like a normal function:
However, it may also be partially applied to an argument by using not_equal.to
:
In other words, not_equal.to(x)
is a function object that is equivalent to partial(not_equal, x)
. This is provided to enhance the readability of some constructs, especially when using higher order algorithms.
Given a Logical Bool
and two Comparables A
and B
that share a common embedding, the signature is \( \mathtt{not\_equal} : A \times B \to Bool \).
x,y | Two objects to compare for inequality. |