May 14, 2019
On 5/14/19 12:13 AM, H. S. Teoh wrote:
> Moreover, Andrei has mentioned before that opCmp can technically be used
> for implementing partial orders. I had thought otherwise in the past,
> because I only considered opCmp that returns int. However, if opCmp is
> allowed to return float, then you can return float.nan for the
> incomparable case (e.g., two sets that are not subsets of each other)
> and thus achieve a non-linear partial ordering, such as the subset
> relation.
> 
> Whether or not this is a*good*  way of implementing the subset operation
> is a different question, of course. If we restricted opCmp to only
> linear orderings, then this wouldn't be an issue.

opCmp returning float has become a popular D idiom, and banking on that is the right thing to do.
May 14, 2019
On 5/14/19 10:29 PM, Adam D. Ruppe wrote:
> thanks, yeah I missed most of dconf for various reasons (hopefully will catch up when the videos released though) so this is good to read.

Aren't the unedited streams already available?
May 14, 2019
On 5/14/19 10:21 PM, Mike Franklin wrote:
> On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
> 
>> Should `opCmp` return a float?
>>
>> The reason: when we attempt to compare two types that aren't comparable (an unordered relationship) we can return float.NaN. Thus we can differentiate between a valid -1, 0, 1 and an invalid float.NaN comparison.
> 
> Thinking about this a little more, why would the compiler even allow comparing two types that aren't comparable?  Shouldn't that be a compiler error?

Their dynamic types may be comparable.

May 14, 2019
On 5/14/19 10:06 PM, Mike Franklin wrote:
> On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
> 
>> Should `opCmp` return a float?
>>
>> The reason: when we attempt to compare two types that aren't comparable (an unordered relationship) we can return float.NaN. Thus we can differentiate between a valid -1, 0, 1 and an invalid float.NaN comparison.
> 
> Seems like a job for an enum, not a float or an integer.

I repeat myself: this won't work.

Recall that a < b is lowered into a.opCmp(b) < 0. So we have a comparison against the literal 0. For that float works nicely because nan etc etc.
May 14, 2019
On 5/14/19 1:08 AM, Andrei Alexandrescu wrote:
> On 5/14/19 12:36 AM, Seb wrote:
>> On Tuesday, 14 May 2019 at 21:06:05 UTC, Mike Franklin wrote:
>>> On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
>>>
>>>> Should `opCmp` return a float?
>>>>
>>>> The reason: when we attempt to compare two types that aren't comparable (an unordered relationship) we can return float.NaN. Thus we can differentiate between a valid -1, 0, 1 and an invalid float.NaN comparison.
>>>
>>> Seems like a job for an enum, not a float or an integer.
>>>
>>> Mike
>>
>> +1 for enum. As far as I can see you only have four actual states:
>>
>> lower, equal, higher, nonComparable
> 
> This won't work because the result of opCmp is compared against zero. Using a floating point number is likely to be more efficient.

s/likely/also likely/
May 15, 2019
On Wednesday, 15 May 2019 at 00:08:10 UTC, Andrei Alexandrescu wrote:

> This won't work because the result of opCmp is compared against zero. Using a floating point number is likely to be more efficient.

Please consider the fact that some microcontrollers don't have an FPU.  Some may have a software floating point implementation but consider the cost in flash memory consumption and performance implementing such a thing in software.  It seems excessive.

Although it would be much more work, perhaps what is needed is a new type (e.g. `struct CmpResult`) with 4 immutable instances representing each result and an `opCmp` and `opEquals` implementation that does the right thing comparing against 0 or whatever else is needed.  Yes, it's more complicated, but I think it would scale better.

Mike

May 15, 2019
On Wednesday, 15 May 2019 at 00:08:10 UTC, Andrei Alexandrescu wrote:
> On 5/14/19 12:36 AM, Seb wrote:
>> On Tuesday, 14 May 2019 at 21:06:05 UTC, Mike Franklin wrote:
>>> On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
>>>
>>>> Should `opCmp` return a float?
>>>>
>>>> The reason: when we attempt to compare two types that aren't comparable (an unordered relationship) we can return float.NaN. Thus we can differentiate between a valid -1, 0, 1 and an invalid float.NaN comparison.
>>>
>>> Seems like a job for an enum, not a float or an integer.
>>>
>>> Mike
>> 
>> +1 for enum. As far as I can see you only have four actual states:
>> 
>> lower, equal, higher, nonComparable
>
> This won't work because the result of opCmp is compared against zero. Using a floating point number is likely to be more efficient.

A DIP should cite real evidence/data though.

Also, note that all other cases except comparing against NaN require more instructions with a floating point number:

https://d.godbolt.org/z/lwzBVn
May 14, 2019
On 5/14/19 1:23 AM, Mike Franklin wrote:
> On Wednesday, 15 May 2019 at 00:08:10 UTC, Andrei Alexandrescu wrote:
> 
>> This won't work because the result of opCmp is compared against zero. Using a floating point number is likely to be more efficient.
> 
> Please consider the fact that some microcontrollers don't have an FPU.  Some may have a software floating point implementation but consider the cost in flash memory consumption and performance implementing such a thing in software.  It seems excessive.

Writing D code assuming float comparison is prohibitively expensive seems an .

> Although it would be much more work, perhaps what is needed is a new type (e.g. `struct CmpResult`) with 4 immutable instances representing each result and an `opCmp` and `opEquals` implementation that does the right thing comparing against 0 or whatever else is needed.  Yes, it's more complicated, but I think it would scale better.

Not sure there's much to gain there. a < b is lowered to a.opCmp(b) < 0. So then... you define opCmp to return an instance of this:

---
import std.stdio;

struct OverengineeredCmpResult {
    enum R { lt, eq, gt, ionno }
    private R payload;
    int opCmp(int alwaysZero) {
        writeln("b");
        return 0;
    }
}

struct A {
    OverengineeredCmpResult opCmp(A rhs) {
        writeln("a");
        return OverengineeredCmpResult(OverengineeredCmpResult.R.ionno);
    }
}

void main() {
    A a, b;
    if (a < b) {}
}
---

Much ado about nothing.
May 15, 2019
On Wednesday, 15 May 2019 at 00:19:05 UTC, Andrei Alexandrescu wrote:
> I repeat myself: this won't work.
>
> Recall that a < b is lowered into a.opCmp(b) < 0. So we have a comparison against the literal 0.

We could just as well change the definition of the lowering to

a.opCmp(b) == ComparisonResult.lessThan


I understand the generated instructions would be slightly different and that might be relevant, but if we're talking about changing things, no need to arbitrarily draw the line like that.
May 15, 2019
On Wednesday, 15 May 2019 at 00:15:38 UTC, Andrei Alexandrescu wrote:

>> Thinking about this a little more, why would the compiler even allow comparing two types that aren't comparable?  Shouldn't that be a compiler error?
>
> Their dynamic types may be comparable.

Then shouldn't the author be using their comparable dynamic types explicitly when performing a comparison?