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.
- Dominikus Dittes Scherkl (17/24) Jan 12 2016 Interesting. I often use partially-ordered objects in my code,
- 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
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 https://dlang.org/spec/operatoroverloading.html#compare) would now lower to `a.opCmpOrdered(b) && a.opCmp(b) [<>]=? 0`. E.g. `a= b` becomes `a.opCmpOrdered(b) && a.opCmp(b) >= 0`. IfopCmpOrdered 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 overloading to make > and < usable for arbitrary binary 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
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: [...]
Jan 12 2016
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: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?Background: Some important properties for binary relations on sets that are somewhat similar to the normal ≤/≥ on the real numbers or integers are: [...]
Jan 12 2016
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
On Tuesday, 12 January 2016 at 19:00:11 UTC, Andrei Alexandrescu wrote:On 01/12/2016 01:27 PM, John Colvin wrote: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.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
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:s/totality/reflexivity which also implies it can't force totality. Note that a non-reflexive <= doesn't imply anything about ==.On 01/12/2016 01:27 PM, John Colvin wrote: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.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
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
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:It might be a bit of a mess, agreed, but nonetheless: assert(!(float.nan <= float.nan));Note that a non-reflexive <= doesn't imply anything about ==.Non-reflexive '<=' does not make any sense at all.
Jan 12 2016
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:Agreed, but in case of float '<=' is not an order at all.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
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: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.On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:Agreed, but in case of float '<=' is not an order at all.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
On Tuesday, 12 January 2016 at 19:50:57 UTC, Fool wrote:On Tuesday, 12 January 2016 at 19:48:35 UTC, Fool wrote: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?On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin 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.On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:Agreed, but in case of float '<=' is not an order at all.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
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:That depends on whether marketing decides to emphasize safety over performance. I'm glad that I'm not in charge! ;-)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
On 01/12/2016 04:06 PM, John Colvin wrote:On Tuesday, 12 January 2016 at 19:50:57 UTC, Fool wrote:We're fine as we are. By default sort compares with "<". -- AndreiOn Tuesday, 12 January 2016 at 19:48:35 UTC, Fool wrote: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?On Tuesday, 12 January 2016 at 19:46:47 UTC, John Colvin 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.On Tuesday, 12 January 2016 at 19:44:18 UTC, Fool wrote:Agreed, but in case of float '<=' is not an order at all.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
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
On Tuesday, 12 January 2016 at 19:28:36 UTC, Andrei Alexandrescu wrote:On 01/12/2016 02:13 PM, John Colvin wrote: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...a<=b and b<=a must also be false.Would the advice "Only use < and == for partially-ordered data" work? -- Andrei
Jan 12 2016
On 01/12/2016 03:01 PM, John Colvin wrote:On Tuesday, 12 January 2016 at 19:28:36 UTC, Andrei Alexandrescu wrote:I'd be in favor of giving people the option to disable the use of <= andOn 01/12/2016 02:13 PM, John Colvin wrote: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...a<=b and b<=a must also be false.Would the advice "Only use < and == for partially-ordered data" work? -- Andrei= for specific data. It's a simple and logical approach. -- Andrei
Jan 12 2016
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. -- AndreiBut doesn't the symbol <= originate from ORing < and = ?
Jan 12 2016
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
On 01/12/2016 03:10 PM, Fool wrote:On Tuesday, 12 January 2016 at 20:04:26 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. AndreiI'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. -- AndreiBut doesn't the symbol <= originate from ORing < and = ?
Jan 12 2016
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. AndreiIt 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
On Tuesday, 12 January 2016 at 20:04:26 UTC, Andrei Alexandrescu wrote:On 01/12/2016 03:01 PM, John Colvin wrote:Having thought about this a bit more, it doesn't fix the problem: 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).On Tuesday, 12 January 2016 at 19:28:36 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. -- AndreiOn 01/12/2016 02:13 PM, John Colvin wrote: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...a<=b and b<=a must also be false.Would the advice "Only use < and == for partially-ordered data" work? -- Andrei
Jan 12 2016
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
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: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 the overloading if we allow that. Having > and < overloaded separately is asking for trouble. Another possibility would be to introduce opCmpEquals(T)(T rhs) to handle [<>]= explicitly.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
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
On Tuesday, 12 January 2016 at 22:28:13 UTC, Andrei Alexandrescu wrote:On 01/12/2016 03:56 PM, John Colvin wrote: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...Please consider the second design I proposed?I don't think it solves a large problem. -- Andrei
Jan 12 2016
On 01/12/2016 06:52 PM, John Colvin wrote:On Tuesday, 12 January 2016 at 22:28:13 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. -- AndreiOn 01/12/2016 03:56 PM, John Colvin wrote:Ok. Would you consider any solution, or is that a "leave it broken"?Please consider the second design I proposed?I don't think it solves a large problem. -- Andrei
Jan 12 2016
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. -- AndreiAlthough 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
On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei Alexandrescu wrote:On 01/12/2016 06:52 PM, John Colvin 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.On Tuesday, 12 January 2016 at 22:28:13 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. -- AndreiOn 01/12/2016 03:56 PM, John Colvin wrote:Ok. Would you consider any solution, or is that a "leave it broken"?Please consider the second design I proposed?I don't think it solves a large problem. -- Andrei
Jan 12 2016
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:https://issues.dlang.org/show_bug.cgi?id=15561[...]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
On Wednesday, 13 January 2016 at 01:43:21 UTC, John Colvin wrote:That's a good point.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
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: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.That's a good point.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
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:https://github.com/D-Programming-Language/phobos/pull/3927On Wednesday, 13 January 2016 at 00:31:48 UTC, Andrei Alexandrescu wrote:https://issues.dlang.org/show_bug.cgi?id=15561[...]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 13 2016
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 though the latest version of my checked integer type actually has no "NaN" state of its own.
Jan 12 2016
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
On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:On 01/12/2016 07:27 PM, John Colvin wrote:Interesting, I'll have to think more about this. Pretty ugly to have to use floating point instructions for every comparison, no matter the actually data, but maybe there's something here......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
On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote:On 01/12/2016 07:27 PM, John Colvin wrote:what about classes and Object.opCmp?...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
On 01/12/2016 10:02 PM, John Colvin wrote:On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote: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.)On 01/12/2016 07:27 PM, John Colvin wrote:what about classes and Object.opCmp?...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
On Tuesday, 12 January 2016 at 21:27:38 UTC, Timon Gehr wrote:On 01/12/2016 10:02 PM, John Colvin wrote:I actually quite like it. Also, checking the generated assembly for an IntWithNaN type: no floating point comparisons in sight (with gdc and ldc at least, don't really care if dmd does something a bit dumb)On Tuesday, 12 January 2016 at 20:52:51 UTC, Timon Gehr wrote: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.)On 01/12/2016 07:27 PM, John Colvin wrote:what about classes and Object.opCmp?...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 13 2016