<p>The comparative relationship between objects is very complicated, there are all comparable, partial comparison, all equal, partial equal situations.</p>
<p>A total of twelve functions are needed. The return value types of these functions are as shown in the following table:</p>
<div class="s-table-container"><table class="s-table">
<thead>
<tr>
<th style="text-align: center;">Sign</th>
<th style="text-align: center;">Partial</th>
<th style="text-align: center;">Total</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;"><code>⩾</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
<tr>
<td style="text-align: center;"><code>&gt;</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
<tr>
<td style="text-align: center;"><code>=</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
<tr>
<td style="text-align: center;"><code>≠</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
<tr>
<td style="text-align: center;"><code>&lt;</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
<tr>
<td style="text-align: center;"><code>⩽</code></td>
<td style="text-align: center;"><code>Option&lt;bool&gt;</code></td>
<td style="text-align: center;"><code>bool</code> |  <code>!</code></td>
</tr>
</tbody>
</table></div>
<ul>
<li><code>!</code> means panic in runtime call</li>
</ul>
<p>But many of them can generate each other, such as <code>=</code> can generate <code>≠</code>, <code>&gt;</code> can generate <code>&lt;</code>, <code>⩾</code> can generate <code>⩽</code>.
​</p>
<h2>Question</h2>
<p>How to define the minimum interface and ensure that there is no conflict?</p>
<p><strong>Consistency</strong> means to prevent accidental implementation errors, such as</p>
<ul>
<li>making <code>⩾</code> always <code>false</code> and <code>=</code> always <code>true</code>.</li>
<li><code>type A = type B</code> always <code>false</code>, <code>type B = type A</code> always <code>true</code>.</li>
</ul>
<p>For example, <code>=</code> and <code>≠</code> can be derived from each other, then if I support <a href="https://doc.rust-lang.org/beta/unstable-book/language-features/negative-impls.html" rel="nofollow noreferrer">Negative Trait</a>, I can prohibit implement of both to ensure consistency.</p>

<language-design>
How to define comparators and maintain consistency
CC BY-SA 4.0
2024-02-28T04:01:12.380Z
What does your notion of "partial comparison" (and "partial equal") mean? A partial order doesn't make comparisons three-valued, it just adds a fourth relationship one value can have to another--"incomparable" meaning "neither greater, less, nor equal", not that none of those three are "even false".
Unrelated String
CC BY-SA 4.0
2024-02-28T04:08:44.327Z
I'm kind of confused by the fact that, except for the far left column, each column has the same text going downwards. Can you expand on what those mean?
David Young
CC BY-SA 4.0
2024-02-28T06:54:00.633Z
What exactly are you asking here? Which minimal set of comparisons is needed to generate all comparisons, like python's [total ordering](https://docs.python.org/3/library/functools.html#functools.total_ordering)? Given all comparisons how to enforce/validate the results are consistent? Given that there are different orders, how to encode the different constraints/freedoms of each?
MisterMiyagi
CC BY-SA 4.0
2024-02-28T10:54:11.943Z
The minimum interface would be one method taking two arguments, returning either "less", "equal" or "more" (or "uncomparable" if you allow that). That's patently minimal because the interface for comparing two things can't be smaller than one method with two arguments. But this interface does exist in plenty of languages, e.g. Java and JavaScript use a negative, zero or positive return value for the three cases.
kaya3
CC BY-SA 4.0
2024-02-28T11:13:39.843Z
Maybe this question conflates implementing a *partial order*, with seeing the comparison function as a *partial function* (modeled as returning Option)? I mean, in addition to the problem of using bool instead of a three-valued {less,greater,equal} result for the comparison function.
Eldritch Conundrum
CC BY-SA 4.0
2024-02-28T18:31:26.673Z
You seem to be talking about Rust specifically. Nothing says in some other language that `>=` alone has to be anything other than total. In Haskell, e.g., `>=` and `==` (along with `not`) are sufficient to define the other 4, and partiality can be handled by using `fmap @Maybe` and `pure @Maybe`.
chepner
CC BY-SA 4.0
2024-03-17T19:00:20.820Z
@kaya3 <, =, > can all be defined in terms of ≤: a = b ↔ a ≤ b ∧ b ≤ a, a < b ↔ a ≤ b ∧ ¬(b ≤ a), a > b ↔ b < a.
user76284
<p>One solution to this problem is C++'s operator&lt;=&gt; (a.k.a spaceship oerator).</p>
<p>There is a detailed description in the <a href="https://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0515r0.pdf" rel="noreferrer">P0515 proposal</a>, and shorter descriptions <a href="https://en.cppreference.com/w/cpp/language/default_comparisons" rel="noreferrer">in cppreeference</a> and <a href="https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading/77055231#77055231">this C++faq answer</a> (section on Default Comparisons (C++20)).</p>
<p>The topic is somewhat complex in its details [it's C++ ;-)], but on the surface:</p>
<ul>
<li>you define an <code>operator&lt;=&gt;(sometype y)</code> in-class, or two-parameter stand-alone, possibly <code>friend</code>;</li>
<li>its return type (<code>std::strong_ordering</code>, <code>std::weak_ordering</code>, or <code>std::partial_ordering</code>) is used by the compiler to decide which other comparison operators to generate automatically;</li>
<li><code>operator&lt;=&gt;()</code> can be <code>default</code>ed, then the compiler will generate it automatically (e.g. by memberwise comparison).</li>
</ul>

CC BY-SA 4.0
2024-02-28T22:16:19.783Z
2024-02-28T22:16:19.783Z
Pablo H
<p>This is a peeve of mine. In C# for example, in order to implement a new type that has equality and inequality operators you end up having to override the <code>==</code>, <code>!=</code>, <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>, <code>&gt;=</code> operators, and override the <code>Equals</code> method, and implement <code>IComparable&lt;T&gt;</code>, and get the hashing logic right. It's a lot.</p>
<p>We could make it a lot easier. It would be nice that if you said:</p>
<pre><code>class X : IComparable&lt;X&gt;
{
  public int CompareTo(X other)
  { 
    // return 0, 1 or -1 depending on comparison
  }
</code></pre>
<p>that the compiler would pretend that you had also written all the boilerplate:</p>
<pre><code>  public static bool operator &lt;(X a, X b) =&gt; a.CompareTo(b) &lt; 0;
  public static bool operator &gt;(X a, X b) =&gt; a.CompareTo(b) &gt; 0;
  ... and so on ...
</code></pre>
<p>It's not hard boilerplate to write, but it would be nice if you didn't have to.</p>
<p>However this sort of feature is very low priority because it doesn't add new representational power or solve a major pain point. It's low bang for buck, so I wouldn't expect this sort of thing soon in C#. If you're designing a new language though, go for it.</p>

CC BY-SA 4.0
2024-02-28T04:25:34.860Z
2024-02-28T04:25:34.860Z
Eric Lippert
CC BY-SA 4.0
2024-02-28T11:09:32.033Z
Does this work for partial orders where both `a < b` and `b > a` may be false *without* `a == b` being true? What would `CompareTo` return in this case?
MisterMiyagi
CC BY-SA 4.0
2024-02-28T12:42:55.567Z
For partially ordered types, an equivalent to `CompareTo` would have to have four possible return values, see [my answer](https://langdev.stackexchange.com/a/3606/161) for one possible example. At any rate, you can't use the simple rule that `a`$R$`b`$\iff$`a.CompareTo(b)`$R\,0$ when it comes to partially ordered types.
Jasmijn
CC BY-SA 4.0
2024-02-28T16:08:33.233Z
In Kotlin, the comparison operators are indeed overridden by `compareTo`
Seggan
CC BY-SA 4.0
2024-02-29T01:33:07.087Z
@MisterMiyagi: `IComparable` is for implementing total orders; it's for sorting a list of stuff. If you've got some exotic scenario where you're topologically sorting a lattice or some such, that's not the mechanism for you.
Eric Lippert
<p><strong>TL;DR: What, not How.</strong></p>
<p>There is one further important relationship: that of hashing and equality. You really want the hash operation and the equal operation to operate on the same set of fields.</p>
<p>While a single <code>compare</code> method, returning a (potentially partial) order can help with all the comparison operators, it does not solve the problem of having a hash operation consistent with the equal operation.</p>
<p>The simplest way to achieve consistency, thus, is to have the users specify the <em>fields</em> that participate in the equality/ordering, instead of how to hash or compare.</p>
<p>Note that unlike hashing, however, a simple visitation method is not quite suitable for <em>comparison</em>, and instead you'd want a method which returns an iterator over the fields, so the framework can ask for the first field to compare of each type, then the second field, etc...</p>
<p>Let's have an example in Python:</p>
<pre class="lang-py prettyprint-override"><code>class User:
    def __init__(self, name: String, age: int):
        self.__name = name
        self.__age = age

    def id(self) -&gt; Iterator[Any]:
        yield self.__name
        yield self.__age


def hash(object: Any, hasher: Any) -&gt; int:
    &quot;&quot;&quot;Computes the hash of `object` as per the `hasher` incremental algorithm.&quot;&quot;&quot;

    #    User override
    hash_override = getattribute(object, '__hash__', None)

    if callable(hash_override):
        hash_override(object, hasher)
    else:
        for field in object.id():
            hash(field, hasher)

    return hasher.finalize()


def compare(left: Any, right: Any) -&gt; int:
    &quot;&quot;&quot;Returns 0 for equal, -1 for left &lt; right, +1 for left &gt; right.&quot;&quot;&quot;

    #    User override
    comparator = getattribute(left, '__cmp__', None)

    if callable(comparator):
        return comparator(left, right)

    for left_field, right_field in zip_longest(left.ids(), right.ids()):
        result = compare(left_field, right_field)

        if result == 0:
            continue

        return result

    return 0
</code></pre>
<p>It should generally be possible to implement the scheme in a static language, though how to will depend on the capabilities of said language:</p>
<ul>
<li>In Java, one would use <code>Object</code> as the central key of the scheme.</li>
<li>In Rust, one would create an <em>associated type</em> representing the sequence of fields to iterate through.</li>
</ul>

CC BY-SA 4.0
2024-03-13T11:32:19.357Z
2024-03-13T11:32:19.357Z
Matthieu M.
<p>Many examples of more complicated &quot;comparables&quot; or &quot;comparators&quot;, often in statically typed languages, have been given, so let's take a look at languages that aim to be <em>simple</em>.</p>
<p>First, comparators should not be tied to objects. I want to be able to sort the same people both by age and name. Thus, we need some kind of &quot;comparator&quot; interface. You asked</p>
<blockquote>
<p>How to define the minimum interface and ensure that there is no conflict?</p>
</blockquote>
<p>The simplest way is to just require a <code>function(a, b T) bool</code>, a &quot;less than&quot; function, as a comparator. This function should return <code>true</code> if and only if <code>a &lt; b</code>. This is what Lua's and Go's sort functions do.</p>
<p>Now developers just need to implement one function correctly. The rest follows from the definition of &quot;less than&quot;:</p>
<ul>
<li>If <code>a &lt; b</code>, then <code>a</code> is less than <code>b</code>.</li>
<li>If <code>b &lt; a</code>, then <code>a</code> is greater than <code>b</code>.</li>
<li>Otherwise, <code>a</code> and <code>b</code> must be equal (assuming a total order)</li>
</ul>
<p>Want to sort by age? Simply pass <code>function(a, b) return a.age &lt; b.age end</code>.</p>
<p>The advantage of this approach is that it's about as simple as can be - no &quot;spaceship&quot; operator, no need for an &quot;enum&quot; - or worse, signed number, which invites using arithmetic, which runs the risk of overflows - return type, no need for a &quot;comparator&quot; interface/trait, just a simple boolean function.</p>
<p>Worth mentioning here: Python functions that require an ordering let you pass a <code>key</code> function (rather than a comparator). This is often slightly more convenient and reduces the potential for mistakes - often you just want to sort by a property (perhaps one that you haven't calculated yet), or a bunch of properties (in which case you can pack them up in a tuple).</p>
<p>There are downsides to this approach, though. For one it can be slightly less efficient due to having to make (up to) two calls (but only 1.5 on average, assuming <code>&lt;</code> and <code>&gt;</code> appear equally often and <code>==</code> is rare) calls to the &quot;comparator&quot; rather than a single call. It also assumes a total order, usually. I find that this is not a major limitation, though. The most common applications where orders are needed require a total order. Floats may technically have NaN, but nobody would expect sorting a list with NaNs in it to produce a sensible result; the NaNs ending up in the list would be considered a bug, and it is welcomed if sorting throws an error.</p>
<p>Now, Lua also has &quot;metamethods&quot; to <a href="https://www.lua.org/manual/5.4/manual.html#2.4" rel="nofollow noreferrer">redefine the behavior of the comparison operators for tables</a>. Some things Lua did right here:</p>
<ul>
<li>There are no separate metamethods for <code>~=</code>, <code>&gt;</code> or <code>&gt;=</code>. <code>a ~= b</code> is just syntactic sugar for <code>not (a == b)</code>. It does not let programmers redefine <code>~=</code> to potentially be inconsistent with <code>==</code>. <code>a &gt; b</code> and <code>a &gt;= b</code> are just syntactic sugar for <code>b &lt; a</code> and <code>b &lt;= a</code> respectively as well.</li>
<li><code>==</code> is special: If objects are &quot;primitively equal&quot; (equal by reference), they are always equal and the metamethod is not called. This also makes sense (and saves the programmer from writing the <code>if rawequal(a, b) then return true end</code> optimization boilerplate themselves). One thing that is debatable is that Lua only calls the metamethod if both operands are tables (/ full userdata) - that is, if you implement a custom number type (say bigints), you can't implement comparisons against primitive numbers (this is relevant to performance, however: Checks can be more efficient if there is no need to check the metatable; this does not apply to <code>&lt;</code>, because a &quot;raw&quot; table <code>&lt;</code> primitive is of course an error, whereas a &quot;raw&quot; table <code>==</code> primitive is just <code>false</code>). It is also inconsistent with hashing, as there is no way to provide a custom &quot;hash&quot; metamethod.</li>
<li>This leaves us with <code>&lt;</code> and <code>&lt;=</code> metamethods. Older Lua versions would &quot;emulate&quot; <code>a &lt;= b</code> using <code>not (a &lt; b)</code> if it was missing, automatically ensuring consistency, newer Lua versions (5.4+) don't, probably for two reasons: It makes it easier to understand which metamethods are called and why, and it is potentially slightly better for performance.</li>
<li><code>==</code> could be emulated with <code>&lt;</code> or <code>&lt;=</code> in principle. But this is not a good idea for performance. <code>==</code> can often be faster than <code>&lt;</code> or <code>&lt;=</code> - for example all (only &quot;short&quot; ones in newer versions) strings in Lua are interned, so string comparison for equality is O(1), whereas lexicographic comparison can very well be O(n).</li>
</ul>
<p>Another interesting case is Haskell. If you look at Haskell's <a href="http://zvon.org/other/haskell/Outputprelude/Ord_c.html" rel="nofollow noreferrer">Ord typeclass</a>, it gives you a &quot;best of both worlds&quot;: Either <code>&lt;=</code> or <code>compare</code> are enough for a &quot;minimal complete definition&quot; such that the compiler fills out the rest.</p>
<p><strong>To recap:</strong> A single <code>&lt;</code> <em>or</em> <code>&lt;=</code> function in principle suffices to &quot;emulate&quot; all other comparison methods (for a total order). There are tradeoffs, which is why you might want to allow for multiple, technically &quot;redundant&quot; implementations for some of these methods. A &quot;spaceship&quot;-style operator can efficiently unify the comparison operations, but is not as simple as can be.</p>
<p>Some languages have facilities to autogenerate these (like <code>#derive</code> in Rust) or implementations which let you order by (computed) &quot;key&quot; (like Python).</p>

CC BY-SA 4.0
2024-03-14T23:23:14.020Z
2024-03-15T12:37:03.210Z
2024-03-15T12:37:03.210Z
Luatic
CC BY-SA 4.0
2024-03-15T02:37:46.437Z
*"a > b and a >= b are just syntactic sugar for a < b and a <= b respectively as well."* ─ this seems like an editing error, but I'm not sure if you mean `b < a` and `b <= a` (but presumably with `a` is still evaluated before `b`), or `not (a <= b)` and `not (a < b)` respectively.
kaya3
CC BY-SA 4.0
2024-03-15T12:38:34.297Z
@kaya3 Thanks for pointing that out, I mean `b < a` and `b <= a`, edited. Whether `a` is evaluated before `b` is to my knowledge not defined, see http://lua-users.org/lists/lua-l/2006-06/msg00378.html.
Luatic
<p>There are a number of approaches, depending on the intended use cases and priorities of a language.</p>
<p>One way to think about this is that these are all just derivatives of a single <code>compare</code> method.</p>
<p>For example (in a Rust-like pseudo code):</p>
<pre><code>enum Comparison = Lesser | Equal | Greater | Incomparable

trait Comparable {
    compare: (Self, Self) -&gt; Comparison
}
</code></pre>
<p>... which can fill out the different operators like so:</p>
<pre><code>a &lt; b  &lt;==&gt;  compare(a, b) is Lesser
a &gt; b  &lt;==&gt;  compare(a, b) is Greater
a = b  &lt;==&gt;  compare(a, b) is Equal
a ⩾ b  &lt;==&gt;  compare(a, b) is one of Greater | Equal
a ⩽ b  &lt;==&gt;  compare(a, b) is one of Lesser | Equal
a ≠ b  &lt;==&gt;  compare(a, b) is one of Lesser | Greater | Incomparable
</code></pre>
<p>This is consistent as long as <code>compare</code> is implemented correctly, because they all derive from a single method.</p>
<p>This handles both partially ordered types and totally ordered types, totally ordered types simply never returning <code>Incomparable</code> from <code>compare</code> (although you might want to be able to specify totally ordered types, to allow users to make use of properties like &quot;exactly one of these statements: <code>a &lt; b</code>, <code>a ⩾ b</code> is true&quot;).</p>
<p>You can even use it to implement equality checking for non-ordered type (by only returning <code>Equal</code> or <code>Incomparable</code> from <code>compare</code> -- although, again, it might make sense to separate that out too).</p>
<hr />
<p>I don't believe comparison functions generally return <code>None</code> or raise an error if <code>a</code> and <code>b</code> are of the right type but are simply incomparable. That is because <code>&lt;</code> and friends are mathematically not functions but relations, which are generally implemented in programming language as: for any relation <span class="math-container">$R$</span>, the implementation function <span class="math-container">$f_R$</span> is defined such that <span class="math-container">$f_R(x, y)$</span> returns <code>true</code> if <span class="math-container">$xRy$</span> holds, otherwise it returns <code>false</code>.</p>

CC BY-SA 4.0
2024-02-28T12:37:55.627Z
2024-02-28T12:37:55.627Z
Jasmijn
CC BY-SA 4.0
2024-02-28T17:23:14.723Z
In this case, are total ordering and total equal considered an author's guarantee? That is, a Marker Trait that does not actually need to be implemented?
Aster
CC BY-SA 4.0
2024-02-28T20:15:29.267Z
They could be, but depending on how the language works exactly, you could for example guarantee a total ordering by statically checking that totally ordered types always return the subtype `Lesser | Equal | Greater`. If that's a property you want your language to have it might be simpler to look at a language like Rust, which has [`partial_cmp`](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) returning `Option<Ordering>`  and [`cmp`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) returning `Ordering` separately, [...]
Jasmijn
CC BY-SA 4.0
2024-02-28T20:15:39.990Z
[...] as well as [`eq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) returning `bool`. (Note how `Eq` is a marker trait built on `PartialEq`, requiring no extra code, but only an author's guarantee that `a == a`.)
Jasmijn
CC BY-SA 4.0
2024-03-04T23:23:19.953Z
The problem is, this ignores the commutative properties of comparison -- the required realtionship  between `compare(a, b)` and `compare(b, a)`.  It also ignores transitivity (eq, `compare(a,b) is Equal and compare(b,c) is Equal -> compare(a,c) is Equal`) So one can define a compare operation that meets these requirements, but is nonsensical.
Chris Dodd
CC BY-SA 4.0
2024-03-05T11:17:53.003Z
Yes that is a common trade-off. The only two ways I can think of off the top of my head are to require the programmer to write a formal proof of all the relevant axioms, or to instead map all values that need a `Comparable` implementation to other types that are known to have `Comparable` implementation that satisfies the right axioms. I don't know if the latter is always possible, so then it might be necessary to include an escape hatch with your custom implementation. That would be quite similar to Rust, where you can often derive implementations of these traits.
Jasmijn
# How to define comparators and maintain consistency
Posted by: username_0 on 2024-02-28
The comparative relationship between objects is very complicated, there are all comparable, partial comparison, all equal, partial equal situations.

A total of twelve functions are needed. The return value types of these functions are as shown in the following table:

| Sign | Partial | Total |
| --- | --- | --- |
| `⩾` | `Option` | `bool` | `!` |
| `>` | `Option` | `bool` | `!` |
| `=` | `Option` | `bool` | `!` |
| `≠` | `Option` | `bool` | `!` |
| `<` | `Option` | `bool` | `!` |
| `⩽` | `Option` | `bool` | `!` |

* `!` means panic in runtime call

But many of them can generate each other, such as `=` can generate `≠`, `>` can generate `<`, `⩾` can generate `⩽`.
​

## Question

How to define the minimum interface and ensure that there is no conflict?

**Consistency** means to prevent accidental implementation errors, such as

* making `⩾` always `false` and `=` always `true`.
* `type A = type B` always `false`, `type B = type A` always `true`.

For example, `=` and `≠` can be derived from each other, then if I support [Negative Trait](https://doc.rust-lang.org/beta/unstable-book/language-features/negative-impls.html), I can prohibit implement of both to ensure consistency.
**Tags:** language-design
> **Comments:**
> **username_1**: What does your notion of "partial comparison" (and "partial equal") mean? A partial order doesn't make comparisons three-valued, it just adds a fourth relationship one value can have to another--"incomparable" meaning "neither greater, less, nor equal", not that none of those three are "even false".  
> **username_2**: I'm kind of confused by the fact that, except for the far left column, each column has the same text going downwards. Can you expand on what those mean?  
> **username_3**: What exactly are you asking here? Which minimal set of comparisons is needed to generate all comparisons, like python's [total ordering](https://docs.python.org/3/library/functools.html#functools.total_ordering)? Given all comparisons how to enforce/validate the results are consistent? Given that there are different orders, how to encode the different constraints/freedoms of each?  
> **username_4**: The minimum interface would be one method taking two arguments, returning either "less", "equal" or "more" (or "uncomparable" if you allow that). That's patently minimal because the interface for comparing two things can't be smaller than one method with two arguments. But this interface does exist in plenty of languages, e.g. Java and JavaScript use a negative, zero or positive return value for the three cases.  
> **username_5**: Maybe this question conflates implementing a *partial order*, with seeing the comparison function as a *partial function* (modeled as returning Option)? I mean, in addition to the problem of using bool instead of a three-valued {less,greater,equal} result for the comparison function.  
> **username_6**: You seem to be talking about Rust specifically. Nothing says in some other language that `>=` alone has to be anything other than total. In Haskell, e.g., `>=` and `==` (along with `not`) are sufficient to define the other 4, and partiality can be handled by using `fmap @Maybe` and `pure @Maybe`.  
> **username_7**: @kaya3 <, =, > can all be defined in terms of ≤: a = b ↔ a ≤ b ∧ b ≤ a, a < b ↔ a ≤ b ∧ ¬(b ≤ a), a > b ↔ b < a.  
---
## 5 answers
### Answer by username_10 on 2024-02-28. Score: 5 | Accepted answer
There are a number of approaches, depending on the intended use cases and priorities of a language.
One way to think about this is that these are all just derivatives of a single compare method.
For example (in a Rust-like pseudo code):
enum Comparison = Lesser | Equal | Greater | Incomparable

trait Comparable {
    compare: (Self, Self) -> Comparison
}

... which can fill out the different operators like so:
a < b  <==>  compare(a, b) is Lesser
a > b  <==>  compare(a, b) is Greater
a = b  <==>  compare(a, b) is Equal
a ⩾ b  <==>  compare(a, b) is one of Greater | Equal
a ⩽ b  <==>  compare(a, b) is one of Lesser | Equal
a ≠ b  <==>  compare(a, b) is one of Lesser | Greater | Incomparable

This is consistent as long as compare is implemented correctly, because they all derive from a single method.
This handles both partially ordered types and totally ordered types, totally ordered types simply never returning Incomparable from compare (although you might want to be able to specify totally ordered types, to allow users to make use of properties like "exactly one of these statements: a < b, a ⩾ b is true").
You can even use it to implement equality checking for non-ordered type (by only returning Equal or Incomparable from compare -- although, again, it might make sense to separate that out too).

I don't believe comparison functions generally return None or raise an error if a and b are of the right type but are simply incomparable. That is because < and friends are mathematically not functions but relations, which are generally implemented in programming language as: for any relation $R$, the implementation function $f_R$ is defined such that $f_R(x, y)$ returns true if $xRy$ holds, otherwise it returns false.
> **Comments:**
> **username_0** (2024-02-28): In this case, are total ordering and total equal considered an author's guarantee? That is, a Marker Trait that does not actually need to be implemented?  
> **username_10** (2024-02-28): They could be, but depending on how the language works exactly, you could for example guarantee a total ordering by statically checking that totally ordered types always return the subtype `Lesser | Equal | Greater`. If that's a property you want your language to have it might be simpler to look at a language like Rust, which has [`partial_cmp`](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) returning `Option<Ordering>`  and [`cmp`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) returning `Ordering` separately, [...]  
> **username_10** (2024-02-28): [...] as well as [`eq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) returning `bool`. (Note how `Eq` is a marker trait built on `PartialEq`, requiring no extra code, but only an author's guarantee that `a == a`.)  
> **username_14** (2024-03-04): The problem is, this ignores the commutative properties of comparison -- the required realtionship  between `compare(a, b)` and `compare(b, a)`.  It also ignores transitivity (eq, `compare(a,b) is Equal and compare(b,c) is Equal -> compare(a,c) is Equal`) So one can define a compare operation that meets these requirements, but is nonsensical.  
> **username_10** (2024-03-05): Yes that is a common trade-off. The only two ways I can think of off the top of my head are to require the programmer to write a formal proof of all the relevant axioms, or to instead map all values that need a `Comparable` implementation to other types that are known to have `Comparable` implementation that satisfies the right axioms. I don't know if the latter is always possible, so then it might be necessary to include an escape hatch with your custom implementation. That would be quite similar to Rust, where you can often derive implementations of these traits.  
---
### Answer by username_8 on 2024-02-28. Score: 5
One solution to this problem is C++'s operator<=> (a.k.a spaceship oerator).

There is a detailed description in the [P0515 proposal](https://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0515r0.pdf), and shorter descriptions [in cppreeference](https://en.cppreference.com/w/cpp/language/default_comparisons) and [this C++faq answer](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading/77055231#77055231) (section on Default Comparisons (C++20)).

The topic is somewhat complex in its details [it's C++ ;-)], but on the surface:

* you define an `operator<=>(sometype y)` in-class, or two-parameter stand-alone, possibly `friend`;
* its return type (`std::strong_ordering`, `std::weak_ordering`, or `std::partial_ordering`) is used by the compiler to decide which other comparison operators to generate automatically;
* `operator<=>()` can be `default`ed, then the compiler will generate it automatically (e.g. by memberwise comparison).
---
### Answer by username_9 on 2024-02-28. Score: 4
This is a peeve of mine. In C# for example, in order to implement a new type that has equality and inequality operators you end up having to override the ==, !=, <, >, <=, >= operators, and override the Equals method, and implement IComparable, and get the hashing logic right. It's a lot.
We could make it a lot easier. It would be nice that if you said:
class X : IComparable
{
  public int CompareTo(X other)
  { 
    // return 0, 1 or -1 depending on comparison
  }

that the compiler would pretend that you had also written all the boilerplate:
  public static bool operator <(X a, X b) => a.CompareTo(b) < 0;
  public static bool operator >(X a, X b) => a.CompareTo(b) > 0;
  ... and so on ...

It's not hard boilerplate to write, but it would be nice if you didn't have to.
However this sort of feature is very low priority because it doesn't add new representational power or solve a major pain point. It's low bang for buck, so I wouldn't expect this sort of thing soon in C#. If you're designing a new language though, go for it.
> **Comments:**
> **username_3** (2024-02-28): Does this work for partial orders where both `a < b` and `b > a` may be false *without* `a == b` being true? What would `CompareTo` return in this case?  
> **username_10** (2024-02-28): For partially ordered types, an equivalent to `CompareTo` would have to have four possible return values, see [my answer](https://langdev.stackexchange.com/a/3606/161) for one possible example. At any rate, you can't use the simple rule that `a`$R$`b`$\iff$`a.CompareTo(b)`$R\,0$ when it comes to partially ordered types.  
> **username_11** (2024-02-28): In Kotlin, the comparison operators are indeed overridden by `compareTo`  
> **username_9** (2024-02-29): @MisterMiyagi: `IComparable` is for implementing total orders; it's for sorting a list of stuff. If you've got some exotic scenario where you're topologically sorting a lattice or some such, that's not the mechanism for you.  
---
### Answer by username_12 on 2024-03-13. Score: 1
TL;DR: What, not How.
There is one further important relationship: that of hashing and equality. You really want the hash operation and the equal operation to operate on the same set of fields.
While a single compare method, returning a (potentially partial) order can help with all the comparison operators, it does not solve the problem of having a hash operation consistent with the equal operation.
The simplest way to achieve consistency, thus, is to have the users specify the fields that participate in the equality/ordering, instead of how to hash or compare.
Note that unlike hashing, however, a simple visitation method is not quite suitable for comparison, and instead you'd want a method which returns an iterator over the fields, so the framework can ask for the first field to compare of each type, then the second field, etc...
Let's have an example in Python:
class User:
    def __init__(self, name: String, age: int):
        self.__name = name
        self.__age = age

    def id(self) -> Iterator[Any]:
        yield self.__name
        yield self.__age


def hash(object: Any, hasher: Any) -> int:
    """Computes the hash of `object` as per the `hasher` incremental algorithm."""

    #    User override
    hash_override = getattribute(object, '__hash__', None)

    if callable(hash_override):
        hash_override(object, hasher)
    else:
        for field in object.id():
            hash(field, hasher)

    return hasher.finalize()


def compare(left: Any, right: Any) -> int:
    """Returns 0 for equal, -1 for left < right, +1 for left > right."""

    #    User override
    comparator = getattribute(left, '__cmp__', None)

    if callable(comparator):
        return comparator(left, right)

    for left_field, right_field in zip_longest(left.ids(), right.ids()):
        result = compare(left_field, right_field)

        if result == 0:
            continue

        return result

    return 0

It should generally be possible to implement the scheme in a static language, though how to will depend on the capabilities of said language:

In Java, one would use Object as the central key of the scheme.
In Rust, one would create an associated type representing the sequence of fields to iterate through.
---
### Answer by username_13 on 2024-03-14. Score: 0
Many examples of more complicated "comparables" or "comparators", often in statically typed languages, have been given, so let's take a look at languages that aim to be *simple*.

First, comparators should not be tied to objects. I want to be able to sort the same people both by age and name. Thus, we need some kind of "comparator" interface. You asked

> How to define the minimum interface and ensure that there is no conflict?

The simplest way is to just require a `function(a, b T) bool`, a "less than" function, as a comparator. This function should return `true` if and only if `a < b`. This is what Lua's and Go's sort functions do.

Now developers just need to implement one function correctly. The rest follows from the definition of "less than":

* If `a < b`, then `a` is less than `b`.
* If `b < a`, then `a` is greater than `b`.
* Otherwise, `a` and `b` must be equal (assuming a total order)

Want to sort by age? Simply pass `function(a, b) return a.age < b.age end`.

The advantage of this approach is that it's about as simple as can be - no "spaceship" operator, no need for an "enum" - or worse, signed number, which invites using arithmetic, which runs the risk of overflows - return type, no need for a "comparator" interface/trait, just a simple boolean function.

Worth mentioning here: Python functions that require an ordering let you pass a `key` function (rather than a comparator). This is often slightly more convenient and reduces the potential for mistakes - often you just want to sort by a property (perhaps one that you haven't calculated yet), or a bunch of properties (in which case you can pack them up in a tuple).

There are downsides to this approach, though. For one it can be slightly less efficient due to having to make (up to) two calls (but only 1.5 on average, assuming `<` and `>` appear equally often and `==` is rare) calls to the "comparator" rather than a single call. It also assumes a total order, usually. I find that this is not a major limitation, though. The most common applications where orders are needed require a total order. Floats may technically have NaN, but nobody would expect sorting a list with NaNs in it to produce a sensible result; the NaNs ending up in the list would be considered a bug, and it is welcomed if sorting throws an error.

Now, Lua also has "metamethods" to [redefine the behavior of the comparison operators for tables](https://www.lua.org/manual/5.4/manual.html#2.4). Some things Lua did right here:

* There are no separate metamethods for `~=`, `>` or `>=`. `a ~= b` is just syntactic sugar for `not (a == b)`. It does not let programmers redefine `~=` to potentially be inconsistent with `==`. `a > b` and `a >= b` are just syntactic sugar for `b < a` and `b <= a` respectively as well.
* `==` is special: If objects are "primitively equal" (equal by reference), they are always equal and the metamethod is not called. This also makes sense (and saves the programmer from writing the `if rawequal(a, b) then return true end` optimization boilerplate themselves). One thing that is debatable is that Lua only calls the metamethod if both operands are tables (/ full userdata) - that is, if you implement a custom number type (say bigints), you can't implement comparisons against primitive numbers (this is relevant to performance, however: Checks can be more efficient if there is no need to check the metatable; this does not apply to `<`, because a "raw" table `<` primitive is of course an error, whereas a "raw" table `==` primitive is just `false`). It is also inconsistent with hashing, as there is no way to provide a custom "hash" metamethod.
* This leaves us with `<` and `<=` metamethods. Older Lua versions would "emulate" `a <= b` using `not (a < b)` if it was missing, automatically ensuring consistency, newer Lua versions (5.4+) don't, probably for two reasons: It makes it easier to understand which metamethods are called and why, and it is potentially slightly better for performance.
* `==` could be emulated with `<` or `<=` in principle. But this is not a good idea for performance. `==` can often be faster than `<` or `<=` - for example all (only "short" ones in newer versions) strings in Lua are interned, so string comparison for equality is O(1), whereas lexicographic comparison can very well be O(n).

Another interesting case is Haskell. If you look at Haskell's [Ord typeclass](http://zvon.org/other/haskell/Outputprelude/Ord_c.html), it gives you a "best of both worlds": Either `<=` or `compare` are enough for a "minimal complete definition" such that the compiler fills out the rest.

**To recap:** A single `<` *or* `<=` function in principle suffices to "emulate" all other comparison methods (for a total order). There are tradeoffs, which is why you might want to allow for multiple, technically "redundant" implementations for some of these methods. A "spaceship"-style operator can efficiently unify the comparison operations, but is not as simple as can be.

Some languages have facilities to autogenerate these (like `#derive` in Rust) or implementations which let you order by (computed) "key" (like Python).
> **Comments:**
> **username_4** (2024-03-15): *"a > b and a >= b are just syntactic sugar for a < b and a <= b respectively as well."* ─ this seems like an editing error, but I'm not sure if you mean `b < a` and `b <= a` (but presumably with `a` is still evaluated before `b`), or `not (a <= b)` and `not (a < b)` respectively.  
> **username_13** (2024-03-15): @kaya3 Thanks for pointing that out, I mean `b < a` and `b <= a`, edited. Whether `a` is evaluated before `b` is to my knowledge not defined, see http://lua-users.org/lists/lua-l/2006-06/msg00378.html.  
---
