April 15, 2005
"Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:d3m7do$1bdk$1@digitaldaemon.com...
> It's about time we finally made up our mind what we're going to do with Object.opCmp.

I'm not sure how many people know this but you can compare dynamic arrays. For arrays of objects it winds up calling Object.opCmp. So removing Object.opCmp would make array comparison more complex since presumably the compiler would have to check if it was legal or not. Not that I'm arguing opCmp should stay - just that any argument to remove it needs to look at all the places it is used, propose alternatives and objectively assess the pros and cons.

int main() {
    int[] x,y;

    x = new int[5];
    y = new int[7];
    printf("shorter %d %d %d\n",x<y, x == y, x is y);

    y = new int[5];
    printf("equal   %d %d %d\n",x<y, x == y, x is y);

    x[3] = 10;
    printf("greater %d %d %d\n",x<y, x == y, x is y);

    return 0;
}

prints

shorter 1 0 0
equal   0 1 0
greater 0 0 0

Substitute a class for 'int' above and it continues to work.


April 15, 2005
"Stewart Gordon" <smjg_1998@yahoo.com> wrote in
> Kris wrote:
> > Aye.
> >
> > Supposing the "Three Stooges" were removed ... do you have a suggestion
in
> > terms of implementation? Are you thinking in terms of Interfaces? For example:
> >
> > interface Comparable
> > {
> >   int opEquals (Object o);
> >   int opCmp (Object o);
> > }
>
> Why not
>
>      int opEquals (Comparable o);
>      int opCmp (Comparable o);


The rest of the class was asleep :-)


> Moreover:
>
> - The presence of a concept of equality certainly doesn't correlate directly with the presence of a natural ordering, so let's not pretend it does.


Very true. But if one of them exists for any given class, that object is now exhibiting comparison-traits of the 'deep' variety. If this is done for one, then does it not follow it should be done for the other? In other words, is the Comparable really an indirect indicator of 'deep' comparison? And, does this lead to a suggestion for one method rather than two of them? Is it really /so/ beneficial to have two seperate methods for comparison purposes?

I'm just trying to provoke some thought here; not make assertions.

> - There's been just as much of a debate over whether Object should have opEquals.  My thought is that the default implementation (a given object is equal only to itself) makes sense, and (in theory) makes it possible to use such a class as an AA key.  But this doesn't by itself equate to a cause for either keeping or removing Object.opEquals - the 'equal only to itself' behaviour would still be the default if it's removed.

You must have missed the thread on 'is' vs '==' :-)

Defaulting opEqual() to identity conflicts with its role, and opens the door to both misinterpretation and buggy code. Naturally, there's potential for compromise all over the place. But if opEqual() remains in Object, then it should likely have an assert(0), since the developer should actually be using 'is' instead of '=='. But this is bogus too, since the error should be thrown at compile-time rather than runtime. Thus, opEqual() really should come out the root object, such that appropriate compile-time errors are thrown.


>
> > interface Hashable
> > {
> >   uint toHash();
> > }
> <snip top of upside-down reply>
>
> Would primitive types implement this interface as well?

This is part and parcel of the problem with mixing primitives and aggregates
into a soup. I won't pretend to have an answer for it, but retaining
toHash() within the root object causes technical issues for a compacting GC,
and exhibits similar "shallow vs deep" traits as both opCmp() & opEqual().
That is, if an object has something internal considered worthy of use for
comparison purposes, then it is rather likely to also have something worthy
of hashing (quite possibly the same attribute(s)). Yes?

One can understand why Object has these methods right now ~ it represents the easy way out. It's a shame that it's not also the robust way. I'm no OO bigot, but we're talking about the root object here ~ which, at some level, affects every single class written in D. Applying the "good enough" yardstick to this arena is asking for trouble, IMO. And not just from this NG :-)

This needs some solid engineering; and I'm glad you keep raising the issue, Stewart.

>
> Stewart.
>
> -- 
> My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.


April 15, 2005
BTW, after some thinking, opEquals should do the same; you don't know whether something is equal or not (unless it is identity-equal), so you can't just say it isn't.

To sum up, I believe these should be the default implementations:

int opCmp(Object o)
{
// basically doesn't work, but explains what should be done
    if (this==o) return 0;
    if (this.classinfo==o.classinfo) {
        throw new SomeException("You need to define opCmp(Object) for "~o.classinfo.name);
    } else {
        throw new SomeException("You need to define opCmp(Object) in " ~ this.classinfo.name ~ " that can compare with " ~ o.classinfo.name);
    }
}

int opEquals(Object o)
{
// basically doesn't work, but explains what should be done
    if (this is o) return 1;
    if (this.classinfo==o.classinfo) {
        throw new SomeException("You need to define opEquals(Object) for "~o.classinfo.name);
    } else {
        throw new SomeException("You need to define opEquals(Object) in  " ~ this.classinfo.name ~ " that can test for equality with " ~ o.classinfo.name);
    }
}

uint toHash()
{
// basically works, but is utterly bad; still, if you compile properly
// it will tell you there is a problem
    version(RUNTIME_LINT) {
        throw new PerformanceException("The default toHash() method is being used for class " ~ this.classinfo.name ~". It could not be more inefficient, so you should implement a better one.");
    }
    return 0;
}


xs0

Stewart Gordon wrote:
> xs0 wrote:
> <snip>
> 
>> I'd leave things as is, but redefine
>>
>> int opCmp(Object o)
>> {
>>     if (this is o)
> 
> 
> Why not
> 
>     if (this == o)
> 
> so that if one has defined opEquals, it'll get the message?
> 
>>         return 0;
>>
>>     throw new SomeException("You need to define opCmp(Object) for " ~ o.classinfo.name);
>> }
> 
> <snip>
> 
> Why should applications tell their users to define opCmp(Object)?
> 
> Stewart.
> 
April 15, 2005
> 2. Leave the current DMD behaviour in, and document it properly.

I would add 2a: leave it in, document it and add a warning to dmd and dlint
for classes that define an opCmp that shadows Object.opCmp.
In fact a general warnings about shadowing member functions in base classes
should say "Foo.opCmp(Foo) shadows Object.opCmp(Object)". For the common
case of Object.opCmp it should give a nicer warning that should say
something like "Foo.opCmp(Foo) shadows Object.opCmp(Object). Consider
defining Foo.opCmp(Object) for associative arrays, sorting and other uses".


April 15, 2005
In article <d3p3gt$12bt$1@digitaldaemon.com>, Kris says...
>
>
>"Stewart Gordon" <smjg_1998@yahoo.com> wrote in
>
>> Moreover:
>>
>> - The presence of a concept of equality certainly doesn't correlate directly with the presence of a natural ordering, so let's not pretend it does.
>
>Very true. But if one of them exists for any given class, that object is now exhibiting comparison-traits of the 'deep' variety. If this is done for one, then does it not follow it should be done for the other? In other words, is the Comparable really an indirect indicator of 'deep' comparison? And, does this lead to a suggestion for one method rather than two of them? Is it really /so/ beneficial to have two seperate methods for comparison purposes?

Good question.  In my experience, I've never created a class that has one method and not the other, but I wouldn't say that's indicative of a rule.  Here's a bad example... say I have a collection of objects that represet classifications.  I might want to know if two objects represent the same classification, but ordering a set of such objects might not make a lot of sense.  Always preferring compile-time to run-time error checking, it would be nice if I could ensure that my codebase wasn't mistakenly trying to order such objects without running a zillion unit tests.

>Defaulting opEqual() to identity conflicts with its role, and opens the door to both misinterpretation and buggy code. Naturally, there's potential for compromise all over the place. But if opEqual() remains in Object, then it should likely have an assert(0), since the developer should actually be using 'is' instead of '=='. But this is bogus too, since the error should be thrown at compile-time rather than runtime. Thus, opEqual() really should come out the root object, such that appropriate compile-time errors are thrown.

It's too bad that template member functions can't be virtual or you could define these methods in Object and put a static assert in them.  What about making Object an abstract base class and declaring these functions but not defining them?


Sean


April 15, 2005
"Ben Hinkle" <bhinkle@mathworks.com> wrote in message news:d3p9qh$17hr$1@digitaldaemon.com...
>> 2. Leave the current DMD behaviour in, and document it properly.
>
> I would add 2a: leave it in, document it and add a warning to dmd
> and dlint for classes that define an opCmp that shadows
> Object.opCmp.
> In fact a general warnings about shadowing member functions in
> base classes should say "Foo.opCmp(Foo) shadows
> Object.opCmp(Object)". For the common case of Object.opCmp it
> should give a nicer warning that should say something like
> "Foo.opCmp(Foo) shadows Object.opCmp(Object). Consider defining
> Foo.opCmp(Object) for associative arrays, sorting and other uses".

But what's the attraction with addressing the symptoms of a disease, when we can prevent the disease?

I'm not being sarcastic, I really want to know why people prefer this approach? Does it have _any_ advantages?


April 15, 2005
"TechnoZeus" <TechnoZeus@PeoplePC.com> wrote in message news:d3oakv$8qc$1@digitaldaemon.com...
> Technically, there should also be a way to indicate "unequal" through the
opCmp operator, but that would probably require the results of opCmp to be an enumeration of something like "less", "equal", "greater", "unordered" so that's probably why the opEquals function has independant functionality by default.

You're right. At the moment, it is not practical to create a class that wraps a float because the comparisons won't be able to handle unordered values. I've been thinking about adding an "opCmpX" or something like that that returns 4 states. If it wasn't defined for a class, it would fall back to opCmp.

The reason opEquals is a separate implementation is because some types can be equal or not equal, but have no concept of ordering.


April 15, 2005
In article <d3pcr2$19r3$1@digitaldaemon.com>, Matthew says...
>
>
>"Ben Hinkle" <bhinkle@mathworks.com> wrote in message news:d3p9qh$17hr$1@digitaldaemon.com...
>>> 2. Leave the current DMD behaviour in, and document it properly.
>>
>> I would add 2a: leave it in, document it and add a warning to dmd
>> and dlint for classes that define an opCmp that shadows
>> Object.opCmp.
>> In fact a general warnings about shadowing member functions in
>> base classes should say "Foo.opCmp(Foo) shadows
>> Object.opCmp(Object)". For the common case of Object.opCmp it
>> should give a nicer warning that should say something like
>> "Foo.opCmp(Foo) shadows Object.opCmp(Object). Consider defining
>> Foo.opCmp(Object) for associative arrays, sorting and other uses".
>
>But what's the attraction with addressing the symptoms of a disease, when we can prevent the disease?
>
>I'm not being sarcastic, I really want to know why people prefer this approach? Does it have _any_ advantages?

It requires zero compiler changes.

- EricAnderton at yahoo
April 15, 2005

Matthew wrote:
> "Ben Hinkle" <bhinkle@mathworks.com> wrote in message news:d3p9qh$17hr$1@digitaldaemon.com...
> 
>>>2. Leave the current DMD behaviour in, and document it properly.
>>
>>I would add 2a: leave it in, document it and add a warning to dmd and dlint for classes that define an opCmp that shadows Object.opCmp.
>>In fact a general warnings about shadowing member functions in base classes should say "Foo.opCmp(Foo) shadows Object.opCmp(Object)". For the common case of Object.opCmp it should give a nicer warning that should say something like "Foo.opCmp(Foo) shadows Object.opCmp(Object). Consider defining Foo.opCmp(Object) for associative arrays, sorting and other uses".
> 
> 
> But what's the attraction with addressing the symptoms of a disease, when we can prevent the disease?
> 
> I'm not being sarcastic, I really want to know why people prefer this approach? Does it have _any_ advantages? 
> 
> 

You can sort an Object[] without caring what the actual types are? (of course, as long as all classes involved implement opCmp(Object))

xs0
April 15, 2005
In article <d3pg6t$1c8i$1@digitaldaemon.com>, xs0 says...
>> 
>> I'm not being sarcastic, I really want to know why people prefer this approach? Does it have _any_ advantages?
>
>You can sort an Object[] without caring what the actual types are? (of course, as long as all classes involved implement opCmp(Object))

But is this an advantage?  I've personally never found a reason to compare to entirely unrelated types.  Type safety is a Good Thing.

Sean