## digitalmars.D - opCmp, [partial/total/pre]orders, custom floating point types etc.

• John Colvin (108/109) Jan 12 2016 Background:
• Ilya Yaroshenko (2/7) Jan 12 2016 http://dlang.org/phobos/std_math.html#.cmp ? --Ilya
• John Colvin (9/17) Jan 12 2016 That doesn't solve the whole problem, because std.math.cmp isn't
• Andrei Alexandrescu (4/5) Jan 12 2016 The way I look at it, a partial order would implement opCmp and opEqual
• John Colvin (7/14) Jan 12 2016 a<=b and b<=a must also be false. That would work for a partial
• John Colvin (4/19) Jan 12 2016 s/totality/reflexivity
• Fool (2/3) Jan 12 2016 Non-reflexive '<=' does not make any sense at all.
• John Colvin (3/6) Jan 12 2016 It might be a bit of a mess, agreed, but nonetheless:
• Fool (2/6) Jan 12 2016 Agreed, but in case of float '<=' is not an order at all.
• Fool (4/12) Jan 12 2016 By the way, that implies that the result of sorting an array of
• John Colvin (4/17) Jan 12 2016 Didn't think of that. Yikes. Should we change the default
• Fool (4/11) Jan 12 2016 That depends on whether marketing decides to emphasize safety
• Andrei Alexandrescu (2/18) Jan 12 2016 We're fine as we are. By default sort compares with "<". -- Andrei
• Andrei Alexandrescu (3/4) Jan 12 2016 Would the advice "Only use < and == for partially-ordered data" work? --...
• John Colvin (14/18) Jan 12 2016 If by that you mean "Only use <= or >= on data that defines a
• Andrei Alexandrescu (2/19) Jan 12 2016
• Fool (3/6) Jan 12 2016 But doesn't the symbol <= originate from ORing < and = ?
• Fool (2/3) Jan 12 2016 '=' in the mathematical sense.
• Andrei Alexandrescu (8/12) Jan 12 2016 D uses !(b < a) for a <= b. We can invent notation to disallow that rewr...
• Fool (9/17) Jan 12 2016 It is perfectly fine to use !(b < a) for a <= b. But as John has
• John Colvin (19/50) Jan 12 2016 Having thought about this a bit more, it doesn't fix the problem:
• tsbockman (13/18) Jan 12 2016 If non-total ordering is going to be supported, I don't
• John Colvin (9/27) Jan 12 2016 I would kindof like that (it would definitely allow me to do what
• Andrei Alexandrescu (2/3) Jan 12 2016 I don't think it solves a large problem. -- Andrei
• John Colvin (10/13) Jan 12 2016 Ok. Would you consider any solution, or is that a "leave it
• Andrei Alexandrescu (5/11) Jan 12 2016 I'd leave it to a named function. Using the built-in comparison for
• tsbockman (8/12) Jan 12 2016 Although I would have use for "exotic orderings" in some of my
• John Colvin (11/25) Jan 12 2016 I would completely agree, except that we have builtin types that
• John Colvin (2/15) Jan 12 2016 https://issues.dlang.org/show_bug.cgi?id=15561
• tsbockman (2/5) Jan 12 2016 That's a good point.
• John Colvin (2/20) Jan 13 2016 https://github.com/D-Programming-Language/phobos/pull/3927
• tsbockman (8/9) Jan 12 2016 This also affects any custom numeric type which should be
• Timon Gehr (13/14) Jan 12 2016 struct S{
• John Colvin (4/18) Jan 12 2016 Interesting, I'll have to think more about this. Pretty ugly to
• John Colvin (2/16) Jan 12 2016 what about classes and Object.opCmp?
• Timon Gehr (4/23) Jan 12 2016 You can introduce a new opCmp signature in your subclass, but == is
• John Colvin (5/30) Jan 13 2016 I actually quite like it. Also, checking the generated assembly
John Colvin <john.loughran.colvin gmail.com> writes:
```Background:
Some important properties for binary relations on sets that are
somewhat similar to the normal ≤/≥ on the real numbers or
integers are:

a ≤ a (reflexivity);
if a ≤ b and b ≤ a, then a = b (antisymmetry);
if a ≤ b and b ≤ c, then a ≤ c (transitivity);
a ≤ b or b ≤ a (totality, implies reflexivity);

Definitions:
A preorder obeys reflexivity and transitivity.
A partial order obeys reflexivity, transitivity and antisymmetry.
A total order obeys transitivity, antisymmetry and totality.
A total preorder obeys transitivity and totality but not
antisymmetry

Examples:
Arrays ordered by length, vectors ordered by euclidian length,
complex numbers ordered by absolute value etc. are all total
preorders.
Integers with ≤ or ≥ form a total order.
float/double/real obey antisymmetry and transitivity but not
reflexivity or totality.

Implementations in D:
Total order: opCmp with "consistent" opEquals to enforce
antisymmetry.

Total preorder: opCmp with "inconsistent" opEquals to break
antisymmetry.

Preorder or partial order: not possible in D, opCmp insists on
totality.

Antisymmetry and transitivity but not reflexivity or totality,
e.g. custom float: not possible in D, opCmp insists on totality
(no way for opCmp to signify nan comparisons, either with nan
(reflexivity) or others (totality & reflexivity)).

Solutions to the above problems:

1) opCmp - or some extended, renamed version of it - needs 4
return values: greater, lesser, equal and
neither/unequal/incomparible. This would be the value that is
returned when e.g. either side is nan.

or, less intrusively and more (runtime) efficiently:

2) Introduce a new special function `bool opCmpOrdered(T rhs)`
that, if defined, is used to shortcircuit a comparison. Any
previous lowering to `a.opCmp(b) [<>]=? 0` (as in
now lower to `a.opCmpOrdered(b) && a.opCmp(b) [<>]=? 0`. E.g. `a
= b` becomes `a.opCmpOrdered(b) && a.opCmp(b) >= 0`. If

opCmpOrdered isn't defined the lowering is unchanged from before
(or opCmpOrdered defaults to true, same thing...).

Bigger example: a custom float type

struct MyFloat
{
// ...
bool isNaN() { /* ... */ }
bool opCmpOrdered(MyFloat rhs)
{
if (this.isNaN || rhs.isNaN) return false;
else return true;
}
int opCmp(MyFloat rhs)
{ //can assume neither are nan
/* ...  */
}
bool opEquals(MyFloat rhs)
{
if (this.isNaN || rhs.isNaN) return false;
else /* ... */
}
}

unittest
{
MyFloat a, b; // has .init as nan, of course :)

static allFail(MyFloat a, MyFloat b)
{
// all of these should short-circuit because
// opCmpOrdered will return false
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}

allFail(a, b);
a = 3;
allFail(a, b);

b = 4;
assert(a!=b);
assert(a<b);
assert(a<=b);
assert(!(a>b));
assert(!(a>=b));

a = 4;
assert(a==b);
assert(!(a<b));
assert(a<=b);
assert(!(a>b));
assert(a>=b);
}

P.S. This is not just about floats! It is also very useful for
making types that represent missing data (e.g. encapsulating
using int.min for a missing value). I can only come up with
strained examples for preorders and partial orders that I would
want people using < and > for, so I won't speak of them here.

P.P.S. Note that I am *not* trying to extend D's operator
relations, like in C++. This small change is strictly within the
realm of what <, > and = are already used for (in D, with
floats). I'm convinced that if you wouldn't read it out loud as
something like "less/fewer/smaller than" or "greater/more/bigger
than", you shouldn't be using < or >, you should name a separate
function; I don't think this proposal encourages violating that
principle.
```
Jan 12 2016
Ilya Yaroshenko <ilyayaroshenko gmail.com> writes:
```On Tuesday, 12 January 2016 at 18:27:15 UTC, John Colvin wrote:
Background:
Some important properties for binary relations on sets that are
somewhat similar to the normal ≤/≥ on the real numbers or
integers are:

[...]

http://dlang.org/phobos/std_math.html#.cmp ? --Ilya
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 18:36:32 UTC, Ilya Yaroshenko
wrote:
On Tuesday, 12 January 2016 at 18:27:15 UTC, John Colvin wrote:
Background:
Some important properties for binary relations on sets that
are somewhat similar to the normal ≤/≥ on the real numbers or
integers are:

[...]

http://dlang.org/phobos/std_math.html#.cmp ? --Ilya

That doesn't solve the whole problem, because std.math.cmp isn't
the default comparator you can't use a totally ordered float type
as a drop-in for the builtin float types.

A more interesting question it bring up though is:
does the approach of imposing a (somewhat arbitrary) total order
work for other types where you would normally use a less "strict"
ordering? Does it work well for missing data representations?
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 01:27 PM, John Colvin wrote:
Preorder or partial order: not possible in D, opCmp insists on totality.

The way I look at it, a partial order would implement opCmp and opEqual
such that a < b, b < a, and a == b are simultaneously false for
unordered objects. Would that float your boat? -- Andrei
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 19:00:11 UTC, Andrei Alexandrescu
wrote:
On 01/12/2016 01:27 PM, John Colvin wrote:
Preorder or partial order: not possible in D, opCmp insists on
totality.

The way I look at it, a partial order would implement opCmp and
opEqual such that a < b, b < a, and a == b are simultaneously
false for unordered objects. Would that float your boat? --
Andrei

a<=b and b<=a must also be false. That would work for a partial
order, yes. Unfortunately, that's not possible with the current
opCmp design, hence my 2 suggestions for improvements (I'm pretty
sure the second one is better).

The key thing is to have a design that doesn't enforce totality.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 19:13:29 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:00:11 UTC, Andrei
Alexandrescu wrote:
On 01/12/2016 01:27 PM, John Colvin wrote:
Preorder or partial order: not possible in D, opCmp insists
on totality.

The way I look at it, a partial order would implement opCmp
and opEqual such that a < b, b < a, and a == b are
simultaneously false for unordered objects. Would that float

a<=b and b<=a must also be false. That would work for a partial
order, yes. Unfortunately, that's not possible with the current
opCmp design, hence my 2 suggestions for improvements (I'm
pretty sure the second one is better).

The key thing is to have a design that doesn't enforce totality.

s/totality/reflexivity

which also implies it can't force totality. Note that a
non-reflexive <= doesn't imply anything about ==.
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 19:21:47 UTC, John Colvin wrote:
Note that a non-reflexive <= doesn't imply anything about ==.

Non-reflexive '<=' does not make any sense at all.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:21:47 UTC, John Colvin wrote:
Note that a non-reflexive <= doesn't imply anything about ==.

Non-reflexive '<=' does not make any sense at all.

It might be a bit of a mess, agreed, but nonetheless:

assert(!(float.nan <= float.nan));
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:
Non-reflexive '<=' does not make any sense at all.

It might be a bit of a mess, agreed, but nonetheless:

assert(!(float.nan <= float.nan));

Agreed, but in case of float '<=' is not an order at all.
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 19:48:35 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:
Non-reflexive '<=' does not make any sense at all.

It might be a bit of a mess, agreed, but nonetheless:

assert(!(float.nan <= float.nan));

Agreed, but in case of float '<=' is not an order at all.

By the way, that implies that the result of sorting an array of
float by default comparison is undefined unless the array does
not contain NaN.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 19:50:57 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:48:35 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:
Non-reflexive '<=' does not make any sense at all.

It might be a bit of a mess, agreed, but nonetheless:

assert(!(float.nan <= float.nan));

Agreed, but in case of float '<=' is not an order at all.

By the way, that implies that the result of sorting an array of
float by default comparison is undefined unless the array does
not contain NaN.

Didn't think of that. Yikes. Should we change the default
predicate of std.algorithm.sort to std.math.cmp when
ElementType!R is floating point?
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 21:06:40 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:50:57 UTC, Fool wrote:
By the way, that implies that the result of sorting an array
of float by default comparison is undefined unless the array
does not contain NaN.

Didn't think of that. Yikes. Should we change the default
predicate of std.algorithm.sort to std.math.cmp when
ElementType!R is floating point?

That depends on whether marketing decides to emphasize safety
over performance.

I'm glad that I'm not in charge! ;-)
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 04:06 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:50:57 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:48:35 UTC, Fool wrote:
On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:
Non-reflexive '<=' does not make any sense at all.

It might be a bit of a mess, agreed, but nonetheless:

assert(!(float.nan <= float.nan));

Agreed, but in case of float '<=' is not an order at all.

By the way, that implies that the result of sorting an array of float
by default comparison is undefined unless the array does not contain NaN.

Didn't think of that. Yikes. Should we change the default predicate of
std.algorithm.sort to std.math.cmp when ElementType!R is floating point?

We're fine as we are. By default sort compares with "<". -- Andrei
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 02:13 PM, John Colvin wrote:
a<=b and b<=a must also be false.

Would the advice "Only use < and == for partially-ordered data" work? --
Andrei
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 19:28:36 UTC, Andrei Alexandrescu
wrote:
On 01/12/2016 02:13 PM, John Colvin wrote:
a<=b and b<=a must also be false.

Would the advice "Only use < and == for partially-ordered data"
work? -- Andrei

If by that you mean "Only use <= or >= on data that defines a
total ordering"* I guess it would work, but it has some pretty
big downsides:

1) Annoying to use.
2) You have to use the opCmp return 0 (which normally means
a[<>]=b && b[<>]=a) to mean "not comparable".
3) Not enforceable. Because of 2 you'll always get true if you
use >= or <= on any a pair that doesn't have a defined ordering.
4) inefficient (have to do both < and == separately which can be
a lot more work than <=).

*would be safer to say "types that define", but strictly
speaking...
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 03:01 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:28:36 UTC, Andrei Alexandrescu wrote:
On 01/12/2016 02:13 PM, John Colvin wrote:
a<=b and b<=a must also be false.

Would the advice "Only use < and == for partially-ordered data" work?
-- Andrei

If by that you mean "Only use <= or >= on data that defines a total
ordering"* I guess it would work, but it has some pretty big downsides:

1) Annoying to use.
2) You have to use the opCmp return 0 (which normally means a[<>]=b &&
b[<>]=a) to mean "not comparable".
3) Not enforceable. Because of 2 you'll always get true if you use >= or
<= on any a pair that doesn't have a defined ordering.
4) inefficient (have to do both < and == separately which can be a lot
more work than <=).

*would be safer to say "types that define", but strictly speaking...

I'd be in favor of giving people the option to disable the use of <= and
= for specific data. It's a simple and logical approach. -- Andrei

```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 20:04:26 UTC, Andrei Alexandrescu
wrote:
I'd be in favor of giving people the option to disable the use
of <= and >= for specific data. It's a simple and logical
approach. -- Andrei

But doesn't the symbol <= originate from ORing < and = ?
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 20:10:11 UTC, Fool wrote:
But doesn't the symbol <= originate from ORing < and = ?

'=' in the mathematical sense.
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 03:10 PM, Fool wrote:
On Tuesday, 12 January 2016 at 20:04:26 UTC, Andrei Alexandrescu wrote:
I'd be in favor of giving people the option to disable the use of <=
and >= for specific data. It's a simple and logical approach. -- Andrei

But doesn't the symbol <= originate from ORing < and = ?

D uses !(b < a) for a <= b. We can invent notation to disallow that rewrite.

Anyhow the use of <, >, <=, and >= for partially ordered types is bound
to be less than smooth. Math papers and books often use other notations
(such as rounded or square less-than) to denote operators for partially
ordered data, exactly because denoting them with the classic notation

Andrei
```
Jan 12 2016
Fool <fool dlang.org> writes:
```On Tuesday, 12 January 2016 at 20:25:25 UTC, Andrei Alexandrescu
wrote:
D uses !(b < a) for a <= b. We can invent notation to disallow
that rewrite.

Anyhow the use of <, >, <=, and >= for partially ordered types
is bound to be less than smooth. Math papers and books often
use other notations (such as rounded or square less-than) to
denote operators for partially ordered data, exactly because
denoting them with the classic notation may confuse the reader.

Andrei

It is perfectly fine to use !(b < a) for a <= b. But as John has
pointed out this is sensible only if '<=' is total.

Personally, I'm unsure about the best solution for D. I
understand Walter's argument to 'keep it simple' and do not
support non-total opCmp. On the other hand it is a bit
unsatisfactory that one cannot write a custom type that behaves
like float.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 20:04:26 UTC, Andrei Alexandrescu
wrote:
On 01/12/2016 03:01 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 19:28:36 UTC, Andrei
Alexandrescu wrote:
On 01/12/2016 02:13 PM, John Colvin wrote:
a<=b and b<=a must also be false.

Would the advice "Only use < and == for partially-ordered
data" work?
-- Andrei

If by that you mean "Only use <= or >= on data that defines a
total
ordering"* I guess it would work, but it has some pretty big
downsides:

1) Annoying to use.
2) You have to use the opCmp return 0 (which normally means
a[<>]=b &&
b[<>]=a) to mean "not comparable".
3) Not enforceable. Because of 2 you'll always get true if you
use >= or
<= on any a pair that doesn't have a defined ordering.
4) inefficient (have to do both < and == separately which can
be a lot
more work than <=).

*would be safer to say "types that define", but strictly
speaking...

I'd be in favor of giving people the option to disable the use
of <= and >= for specific data. It's a simple and logical
approach. -- Andrei

It doesn't enable custom float types that are on par with
builtins, doesn't enable transparent "missing-value" types and
doesn't make tsbockmans checked integer types (or other custom
types) work properly and transparently with builtin floats. The
points 1, 2 and 4 from above still stand. Also - the big problem
- it requires antisymmetry, which means no preorders.

One of the great things about D's opCmp and opEquals is that it
separates `a==b` from `a<=b && b<=a`, which enables it to express
types without antisymmetric ordering (see original post for
examples), what you're describing would be a frustrating
situation where you have to choose between breaking antisymmetry
and breaking totality, but never both.

Please consider the second design I proposed? It's small, simple,
has no impact on existing code and works in the right direction
(library types can emulate / act as replacements for builtins) as
opposed to the other way (library types are second class).
```
Jan 12 2016
tsbockman <thomas.bockman gmail.com> writes:
```On Tuesday, 12 January 2016 at 20:56:41 UTC, John Colvin wrote:
Please consider the second design I proposed? It's small,
simple, has no impact on existing code and works in the right
direction (library types can emulate / act as replacements for
builtins) as opposed to the other way (library types are second
class).

If non-total ordering is going to be supported, I don't
understand what's wrong with just allowing this:

bool opCmp(string op, T)(T right) const { }

As an alternative to the current:

bool opEquals(T)(T right) const { }
int opCmp(T)(T right) const { }

Make it a compile-time error for a type to implement both. There
is no need to deprecate the current system - people can even be
encouraged to continue using it, in the very common case where it
can actually express the desired logic.

This approach is simple and breaks no existing code. It is also
optimally efficient with respect to runtime performance.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 21:12:08 UTC, tsbockman wrote:
On Tuesday, 12 January 2016 at 20:56:41 UTC, John Colvin wrote:
Please consider the second design I proposed? It's small,
simple, has no impact on existing code and works in the right
direction (library types can emulate / act as replacements for
builtins) as opposed to the other way (library types are
second class).

If non-total ordering is going to be supported, I don't
understand what's wrong with just allowing this:

bool opCmp(string op, T)(T right) const { }

As an alternative to the current:

bool opEquals(T)(T right) const { }
int opCmp(T)(T right) const { }

Make it a compile-time error for a type to implement both.
There is no need to deprecate the current system - people can
even be encouraged to continue using it, in the very common
case where it can actually express the desired logic.

This approach is simple and breaks no existing code. It is also
optimally efficient with respect to runtime performance.

I would kindof like that (it would definitely allow me to do what
I want, as well as anything else I have failed to notice I need
yet), but it flies quite strongly against Walter's (and mine to
some extent) views that we'll only end up with C++-like abuse of

Another possibility would be to introduce opCmpEquals(T)(T rhs)
to handle [<>]= explicitly.
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 03:56 PM, John Colvin wrote:
Please consider the second design I proposed?

I don't think it solves a large problem. -- Andrei
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 22:28:13 UTC, Andrei Alexandrescu
wrote:
On 01/12/2016 03:56 PM, John Colvin wrote:
Please consider the second design I proposed?

I don't think it solves a large problem. -- Andrei

Ok. Would you consider any solution, or is that a "leave it
broken"?

I think I can find a way around the problem for my purposes in
the short term. However, for other people implementing custom
types I think it is important, it's a dirty corner that needs
sorting out. The more you get to know D, the more of them you
find, the more frustrating it gets seeing they aren't likely to
get fixed...
```
Jan 12 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
```On 01/12/2016 06:52 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 22:28:13 UTC, Andrei Alexandrescu wrote:
On 01/12/2016 03:56 PM, John Colvin wrote:
Please consider the second design I proposed?

I don't think it solves a large problem. -- Andrei

Ok. Would you consider any solution, or is that a "leave it broken"?

I'd leave it to a named function. Using the built-in comparison for
exotic orderings is bound to confuse users. BTW not sure you know, but D
used to have a number of floating point operators like !<>=. Even those
didn't help. -- Andrei
```
Jan 12 2016
tsbockman <thomas.bockman gmail.com> writes:
```On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei
Alexandrescu wrote:
I'd leave it to a named function. Using the built-in comparison
for exotic orderings is bound to confuse users. BTW not sure
you know, but D used to have a number of floating point
operators like !<>=. Even those didn't help. -- Andrei

Although I would have use for "exotic orderings" in some of my
own code, I think this is the right decision.

Really the only reason I'm tempted to say they should be allowed,
is to smooth interaction with floating-point. But, I think what
that really means is that the design of the floating-point
comparisons is bad. (Which is not D's fault, I know.)
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei
Alexandrescu wrote:
On 01/12/2016 06:52 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 22:28:13 UTC, Andrei
Alexandrescu wrote:
On 01/12/2016 03:56 PM, John Colvin wrote:
Please consider the second design I proposed?

I don't think it solves a large problem. -- Andrei

Ok. Would you consider any solution, or is that a "leave it
broken"?

I'd leave it to a named function. Using the built-in comparison
for exotic orderings is bound to confuse users. BTW not sure
you know, but D used to have a number of floating point
operators like !<>=. Even those didn't help. -- Andrei

I would completely agree, except that we have builtin types that
don't obey this rule. I'd be all in favour of sticking with total
orders, but it does make it hard (impossible?) to make a proper
drop-in replacement for the builtin floating point numbers
(including wrappers, e.g. std.typecons.Typedef can't handle nans
correctly) or to properly handle comparisons between custom types
and builtin floating points (as mentioned by tsbockman).

I am all for keeping it simple here, but I still think there's a
problem.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Wednesday, 13 January 2016 at 01:39:26 UTC, John Colvin wrote:
On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei
Alexandrescu wrote:
[...]

I would completely agree, except that we have builtin types
that don't obey this rule. I'd be all in favour of sticking
with total orders, but it does make it hard (impossible?) to
make a proper drop-in replacement for the builtin floating
point numbers (including wrappers, e.g. std.typecons.Typedef
can't handle nans correctly) or to properly handle comparisons
between custom types and builtin floating points (as mentioned
by tsbockman).

I am all for keeping it simple here, but I still think there's
a problem.

https://issues.dlang.org/show_bug.cgi?id=15561
```
Jan 12 2016
tsbockman <thomas.bockman gmail.com> writes:
```On Wednesday, 13 January 2016 at 01:43:21 UTC, John Colvin wrote:
I am all for keeping it simple here, but I still think there's
a problem.

https://issues.dlang.org/show_bug.cgi?id=15561

That's a good point.
```
Jan 12 2016
Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
```On Wednesday, 13 January 2016 at 02:12:36 UTC, tsbockman wrote:
On Wednesday, 13 January 2016 at 01:43:21 UTC, John Colvin
wrote:
I am all for keeping it simple here, but I still think
there's a problem.

https://issues.dlang.org/show_bug.cgi?id=15561

That's a good point.

Interesting. I often use partially-ordered objects in my code,
and therefore define opCmp to return float, making use of the NaN
value. But then I also define opEquals to return false for (NaN
== NaN), and my custom types work as intended.
In fact, the existance of the special floatingpoint operators
like !<> (and beeing able to overload them) was one of the main
reasons for me, to start using D. For me it's a mayor issue if
those operators don't work correct.

I know they are deprecated, but I don't know why. As was pointed
out they are necessary if you want to implement something
partially ordered. That not everybody needs this is no valid
reason to deprecate it.

I hated to be told I should not define opCmp to return float
instead of int, as was also propagated by the "learning D" book.
If this is the common state of the art, I will drop D and start
using my own fork the moment they are not supported anymore.
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Wednesday, 13 January 2016 at 01:43:21 UTC, John Colvin wrote:
On Wednesday, 13 January 2016 at 01:39:26 UTC, John Colvin
wrote:
On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei
Alexandrescu wrote:
[...]

I would completely agree, except that we have builtin types
that don't obey this rule. I'd be all in favour of sticking
with total orders, but it does make it hard (impossible?) to
make a proper drop-in replacement for the builtin floating
point numbers (including wrappers, e.g. std.typecons.Typedef
can't handle nans correctly) or to properly handle comparisons
between custom types and builtin floating points (as mentioned
by tsbockman).

I am all for keeping it simple here, but I still think there's
a problem.

https://issues.dlang.org/show_bug.cgi?id=15561

https://github.com/D-Programming-Language/phobos/pull/3927
```
Jan 13 2016
tsbockman <thomas.bockman gmail.com> writes:
```On Tuesday, 12 January 2016 at 18:27:15 UTC, John Colvin wrote:
P.S. This is not just about floats!

This also affects any custom numeric type which should be
comparable with float - while working on a checked integer type
for Phobos, one of the (minor) problems I have run into is that
it is impossible to reproduce the comparison behaviour of the
built-in integers with respect to floating-point values - even
no "NaN" state of its own.
```
Jan 12 2016
Timon Gehr <timon.gehr gmx.ch> writes:
```On 01/12/2016 07:27 PM, John Colvin wrote:
...

struct S{
auto opCmp(S rhs){ return float.nan; }
bool opEquals(S rhs){ return false; }
}

unittest{
S a,b;
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:
On 01/12/2016 07:27 PM, John Colvin wrote:
...

struct S{
auto opCmp(S rhs){ return float.nan; }
bool opEquals(S rhs){ return false; }
}

unittest{
S a,b;
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}

have to use floating point instructions for every comparison, no
matter the actually data, but maybe there's something here...
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:
On 01/12/2016 07:27 PM, John Colvin wrote:
...

struct S{
auto opCmp(S rhs){ return float.nan; }
bool opEquals(S rhs){ return false; }
}

unittest{
S a,b;
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}

```
Jan 12 2016
Timon Gehr <timon.gehr gmx.ch> writes:
```On 01/12/2016 10:02 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:
On 01/12/2016 07:27 PM, John Colvin wrote:
...

struct S{
auto opCmp(S rhs){ return float.nan; }
bool opEquals(S rhs){ return false; }
}

unittest{
S a,b;
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}

You can introduce a new opCmp signature in your subclass, but == is
enforced to be reflexive for class objects. So this approach only really
works for structs. (And for structs, it is obviously a hack.)
```
Jan 12 2016
John Colvin <john.loughran.colvin gmail.com> writes:
```On Tuesday, 12 January 2016 at 21:27:38 UTC, Timon Gehr wrote:
On 01/12/2016 10:02 PM, John Colvin wrote:
On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:
On 01/12/2016 07:27 PM, John Colvin wrote:
...

struct S{
auto opCmp(S rhs){ return float.nan; }
bool opEquals(S rhs){ return false; }
}

unittest{
S a,b;
assert(!(a==b));
assert(!(a<b));
assert(!(a<=b));
assert(!(a>b));
assert(!(a>=b));
}