May 14, 2019
On 5/14/19 2:03 AM, Adam D. Ruppe wrote:
> So, going back to where the int/float/enum tangent started....
> 
> On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
>> interface Ordered(T)
>> {
>>     int opCmp(scope const T rhs);
>> }
>>
>> Since we are here, I want to raise another question:
>> Should `opCmp` return a float?
> 
> 
> We'd then have two interfaces: Ordered!T, which returns the int, and then PartiallyOrdered!T that can return the float. You pick which one works best for your particular class.

Wait, if we go with the statically-checked solution all interfaces are no longer necessary. Or maybe I'm misunderstanding?
May 15, 2019
On Wednesday, 15 May 2019 at 01:16:56 UTC, Andrei Alexandrescu wrote:
> Wait, if we go with the statically-checked solution all interfaces are no longer necessary. Or maybe I'm misunderstanding?

interfaces are just one strategy for static checking, not a requirement. There'd be nothing special about these.

Realistically, the compiler wouldn't care if there's an interface or not. It just wants to see if the opCmp method is there and can be called, exactly the same as it does for structs.

The interfaces are just a way of communicating this to the user, documenting and formalizing the convention.
May 15, 2019
On Wednesday, 15 May 2019 at 01:02:49 UTC, Andrei Alexandrescu wrote:

> Overall: I dream of a D landscape whereby this is enough of a problem to deserve discussion, a DIP, review, and implementation. As things are there are sufficient things to discuss and improve in D to make this exchange ironic.

This is a fundamental feature of the new proposed object hierarchy on which so much D code will depend.  How is this not worthy of brainstorming, exploration, and consideration?  If it's not done well, and thoroughly, we'll be inventing yet another object hierarchy 5 years from now.  It also serves to educate those participating in and watching this discussion so they understand why things are the way they are.  Then can then spread that knowledge to the next generation of D programmers without having to query those that did that actual work and made the decision.

I'm seeing a lot of interesting ideas coming out of this discussion, and it's exciting and encouraging to know we have so many potential options to choose from and consider.  With a little patience, the dust will settle and something quite nice is likely to emerge.

Mike
May 14, 2019
On 5/14/19 2:38 AM, Mike Franklin wrote:
> On Wednesday, 15 May 2019 at 01:02:49 UTC, Andrei Alexandrescu wrote:
> 
>> Overall: I dream of a D landscape whereby this is enough of a problem to deserve discussion, a DIP, review, and implementation. As things are there are sufficient things to discuss and improve in D to make this exchange ironic.
> 
> This is a fundamental feature of the new proposed object hierarchy on which so much D code will depend.  How is this not worthy of brainstorming, exploration, and consideration?  If it's not done well, and thoroughly, we'll be inventing yet another object hierarchy 5 years from now.  It also serves to educate those participating in and watching this discussion so they understand why things are the way they are.  Then can then spread that knowledge to the next generation of D programmers without having to query those that did that actual work and made the decision.

Of course. Changing how opCmp works does not qualify for all that.

It's all about change vs. addition. We in the D community are obsessed with change; if we only changed a little how that works, it would be so good. Yet changing how things that work work (repetition is intentional) is by definition low-yield; addition is fundamentally more generous and offers unbounded potential.

(Of course changing something that doesn't work is necessary. It's Right Work. Like fixing the shared qualifier.)

In this case, ProtoObject is a carefully considered addition that bypasses a malfunctioning artery - the Object class. But it doesn't change or remove it, because there's much live tissue connected to it. Changing how opCmp works - inserting a little botox during the surgery - is a damaging distraction.
May 15, 2019
On Wednesday, 15 May 2019 at 00:32:32 UTC, Andrei Alexandrescu wrote:

>> 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.

Cool! It actually looks much simpler than I imagined.

Mike
May 14, 2019
On Tue, May 14, 2019 at 09:02:49PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
> Well there would be in some instances. People often implement comparisons as a - b currently, where a and b are int expressions.
[...]

FYI, the result of that is actually incorrect in some cases.  (Consider
what happens when there is overflow involved, such as int.max
- int.min, and remember the type of the result.) So, not exactly the
kind of code we should recommend, let alone bend over backwards to
support.

Int comparisons should be left as built-in operators <, =, >, etc., which then get translated to the correct hardware instructions that don't suffer from the overflow bug.


T

-- 
This is not a sentence.
May 15, 2019
On Tuesday, May 14, 2019 5:24:58 PM MDT H. S. Teoh via Digitalmars-d wrote:
> On Tue, May 14, 2019 at 05:05:02PM -0400, Andrei Alexandrescu via
Digitalmars-d wrote:
> > On 5/14/19 9:37 PM, Mike Franklin wrote:
> [...]
>
> > > IMO, objects should only support reference equality out of the box.
> >
> > So that means "x is y" works but not "x == y"?
>
> That makes sense to me if x and y are two completely unrelated classes. Yes they are "related" in the sense that both inherit from ProtoObject, but they have no meaningful relationship as far as the application domain is concerned.  If the user wants to compare them, let him implement the Comparable interface and the corresponding opCmp(). This shouldn't be in ProtoObject.

Even an interface is a problem, because then that locks in the attributes. Containers and the code in druntime can be templated, and any user code that can't be templated can operate on a base class that implements opCmp (or opEquals or whatever function we're talking about) where that base class is whatever is appropriate for that particular class hierarchy. Code bases could even still have interfaces with opEquals or opCmp if appropriate. We just don't want standard interfaces, because that would lock in the attributes, and not all attributes are going to work for all code bases (a prime example of that being const; requiring const for opEquals, opCmp, toString, or toHash would make it so that lazy initialization pretty much wouldn't work with classes).

By templatizing all of the relevant code in druntime, the attributes will be inferred, and we can define these functions on classes in the same way that we do with structs - just with the caveat that any class derived from a class that defines such a function will be restricted by how the base class defined it. However, whereas Object forces all D classes to stick to a particular set of attributes for those functions, they would only be locked in for that particular class hierarchy, and other class hierarchies could make other choices. And by having the relevant druntime code be templated, standard interfaces shouldn't be necessary, whereas if we created such interfaces, we'd basically be making the same mistake that we made with Object except with a different set of attributes.

- Jonathan M Davis



May 15, 2019
On Tuesday, 14 May 2019 at 20:36:08 UTC, Eduard Staniloiu wrote:
> Now the attributes of `opCmp` will be inferred. The implication of this is that now, if we are in the worst case scenario (comparing two `ProtoObject`s) we can not establish any relationship between the two, the `__cmp` lowering won't be able to compare two.
That's ok. Why should anybody expect that two arbitrary things should have an ordered relation? Is egg > apple? And in what way? Is it heavier? longer? older? better? has more calories?
Stupid.
>
> Since we are here, I want to raise another question:
> Should `opCmp` return a float?
Yes, please!
I need unordered relations so often (see above)!

May 15, 2019
On Tuesday, May 14, 2019 6:24:58 PM MDT Seb via Digitalmars-d wrote:
> 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

Except that this DIP doesn't need to define opCmp's signature - at least not if it's not including interfaces in the design. The rules for opCmp's signature on classes should be able to be the same as it is for structs. Classes derived from a class that defines opCmp will be restricted by the signature on the base class, but the base class should be able to define an opCmp the same way that a struct would, meaning that any spec changes that we might want to make about what opCmp returns or accepts should be able to be completely separate from this DIP.

- Jonathan M Davis



May 15, 2019
On 5/14/19 9:36 PM, Eduard Staniloiu wrote:

> Jonathan's question got us to the point raised: maybe it doesn't make much sense to be able to compare two `ProtoObjects`, so maybe you shouldn't be able to. This would change the interface to
> ```
> interface Ordered(T)
> {
>      int opCmp(scope const T rhs);
> }
> ```
> 
> Now the attributes of `opCmp` will be inferred.

Just wanted to make sure you understand this is not the case. opCmp in this instance is a virtual call, and will NOT have attributes inferred.

There isn't really a way to define an interface for this, nor do you need to.

Just define the opCmp you want in your own interface/base object, and then you can compare those. Almost nobody wants to compare 2 completely unrelated objects.

-Steve