View mode: basic / threaded / horizontal-split · Log in · Help
April 15, 2005
Re: Object.opCmp - about time we came to a decision
"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
Re: Object.opCmp - about time we came to a decision
"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
Re: Object.opCmp - about time we came to a decision
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
Re: Object.opCmp - about time we came to a decision
> 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
Re: Object.opCmp - about time we came to a decision
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
Re: Object.opCmp - about time we came to a decision
"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
Re: Object.opCmp - about time we came to a decision
"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
Re: Object.opCmp - about time we came to a decision
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
Re: Object.opCmp - about time we came to a decision
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
Re: Object.opCmp - about time we came to a decision
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
1 2 3 4 5 6 7
Top | Discussion index | About this forum | D home