A fresh look at comparisons

Yigal Chripun yigal100 at gmail.com
Thu Apr 17 05:37:26 PDT 2008


Janice Caron wrote:
> On 16/04/2008, Yigal Chripun <yigal100 at gmail.com> wrote:
>   
>> OK, I see your point.
>>     
>
> Ta. But you see one of my points. I made more than one.
>
>
>   
>>  If I understood this correctly, the issue is the implicit cast to super.
>>     
>
> This particular issue could be dealt with as you suggest, but there
> are other issues that it doesn't fix. For example - consider how you
> might write an opCmp for complex numbers.
>
>     class Complex
>     {
>         int opCmp(Object o)
>         {
>             Complex c = cast(Complex)o;
>             if (c is null) return false;
>
>             if (this.im == 0 && c.im == 0)
>             {
>                 if (this.re < c.re) return -1;
>                 else if (this.re == c.re) return 0;
>                 else return 1;
>             }
>             else return ????
>         }
>     }
>
> The problem is that complex numbers become UNORDERED when the
> imaginary part (of either number) becomes non-zero. opCmp() has no way
> to express that. Henning Hasemann did suggest making opCmp return an
> enum, instead of an int, so that all four possible returns could be
> indicated, but again, like you, he's solving one problem at a time.
> The whole "fresh look" approach is to solve all of the problems in one
> fell swoop. So, this particular problem would be solved in my scheme
> with
>
>     class Complex
>     {
>         is(this < c, unordered)
>         {
>             if (this.im == 0 && c.im == 0)
>             {
>                 return this.re < c.re;
>             }
>             else return false;
>         }
>     }
>   

the above is a non-issue IMO. personally I HATE error return codes/enums
(that's a C idiom and does _not_ belong in a modern language like D). (a
< b) is an error for unordered types just like  "hello" + 2 is an error.
nothing should be returned is in such a case it doesn't matter if it's
an int or an enum. This is an error and should be dealt as such - either
throw an exception or don't define an opCmp for complex (I prefer the
latter since as you said so yourself, Complex numbers are unordered).
I've previously said that opCmp should not be in Object and should not
be defined for all types. a better design IMO is to have an interface
"Ordered" and maybe also "PartialyOrdered" too. also, in conjunction
with my previous post, they could be defined as annotations instead just
like "explicit".

as an aside, annotations in Java use almost the same syntax as
interfaces. This makes perfect sense since annotations have a lot in
common with interfaces. annotations are special interfaces that provide
additional _non_override-able_ functionality so that all subtypes will
have that added functionality. that means that they can mostly be
implemented today in D for classes. a little bit of syntactic sugar and
support for primitives is all that is needed to have this feature in D.

> And there is /yet/ another problem to consider - your "explicit"
> approach works just fine for opCmp, but it won't work for opEquals.
> The reason is that the default behavior we desire from opCmp is very
> different from the default behavior of opEquals. Consider:
>
>     class A { ... }
>     class B : A { int x; }
>
>     // Your way
>     int opEquals(explicit A a1, explicit A a2);
>
>     B b = new B;
>     B c = new C;
>
>     if (b == c)...
>
> Under your scheme, b would not be comparable for equality with c,
> since there is no explicit equality test.
>
> By contrast, my scheme would generate a default equality test.
> Specifically, b would be considered equal to c if and only if
>
>     (cast(A)b == cast(A)c) && (b.x == c.x)
>
> Of course, in the both schemes, the programmer can always override the
> default, by defining an equals comparison for B, but nonetheless, my
> scheme gets you a fine default, which is likely to be the right thing
> to do much, if not most, of the time.
>   
I'm not sure that this default should be provided. the default for many
languages is that unless you define an opEquals for your type, the
comparison will default to identity comparison and from a reply to a
previous post of mine, apparently this is also true in D.
this is the only sensible default IMO. The problem I see with the
suggested default above is the case for classes that should not be
compared at all.
Do i override and throw an error, or should I use:
bool opEquals(A a, A b) {
  if (a is b) return true;
  return false;
}
there are only two sensible approaches IMHO: unless defined the compiler
replaces "==" with "is" (this will never be wrong) or an error is
produced since there is no explicit equality test. If this is not what I
wanted then the error will remind me that I need to provide the test myself.
OTOH, your solution is very similar to C++ which provides defaults which
not always suit the needs of the programmer which then the programmer
needs to know/remember to override. That makes C++ that much more
difficult for beginners to grok an I've admittedly have been bitten by
that myself.
> In other words, I have presented a comprehensive solution which solves
> many problems at once, not just a single problem. (Maybe that's why I
> have trouble explaining it?)
>   
your suggestion is indeed a comprehensive solution to a very specific
problem. OTOH I'm trying to suggest a more general approach that could
be used generally in D without special treatment syntactically for just
this problem. Basically, my trouble understanding your suggestion is
that I don't see a need for that special treatment as opposed to a more
general solution.



More information about the Digitalmars-d mailing list