A fresh look at comparisons

Yigal Chripun yigal100 at gmail.com
Wed Apr 16 13:34:34 PDT 2008


Janice Caron wrote:
> On 16/04/2008, Yigal Chripun <yigal100 at gmail.com> wrote:
>   
>> Someone on this thread made a very good observation: opCmp should be a
>>  multi-method.
>>  This is an excellent idea and applies to more than just opCmp.
>>
>>  given the following classes:
>>
>> class A {}
>>  class B : A {}
>>
>> class C : A {}
>>
>>  I'd suggest the following syntax for multi-methods: (taken from the
>>  article linked bellow)
>>  void func(virtual  A a1, virtual  A a2);
>>  so both a1 and a2 can also be of any subtype of A.
>>
>>  let's look at some of the comparison variations:
>>  opCmp(A a1, A a2); // compare only instances of A
>>  opCmp(virtual A a1, virtual A a2); // will work on all subtypes
>>  opCmp(virtual B b1, virtual B b2); // this overrides the above more
>>  general function
>>  etc...
>>  I think this can provide all possible combinations needed.
>>     
>
> But it still won't work. Watch.
>
>     class A {}
>     class B : A {}
>     class C : A {}
>
>     opCmp(A a1, A a2) { ... } /* not virtual */
>
>     B b = new B
>     C c = new C
>
>     if (b < c) ...
>
> This will /still/ cast both b and c to A's, and then do A's comparison
> test. That's because B and C will both implicitly cast to A.
>
> The only difference "virtual" will make is in the following situation
>
>     class A {}
>     class B : A {}
>     class C : A {}
>
>     opCmp(virtual A a1, virtual A a2) { ... }
>     opCmp(B a1, C a2) { ... }
>
>     A b = new B
>     A c = new C
>
>     if (b < c)...
>
> now opCmp(B,C) would be called, because it exists. But if opCmp(B,C)
> did /not/ exist, then opCmp(A,A) would be called, whether it was
> virtual or not. That's because "virtual" only enables polymorphism; it
> doesn't disable inheritance.
>
> The desired goal is for opCmp(A,A) /not/ to be called if opCmp(B,C)
> doesn't exist. So far as I can see, mine is the only suggestion on
> this thead which achieves that goal.
>   
OK, I see your point.
If I understood this correctly, the issue is the implicit cast to super.
So, one way to deal with that would be to override the opImplicitCast(A)
to throw an exception but that is maybe to big a restriction. So we need
some sort of specifier to prevent implicit casts. Let's call it
"explicit" for the purpose of this thread.
with explicit and the above classes (it can be generalized for structs
and builtins) you'll get:
A a = new B; // classic use of polymorphism in OOP
explicit A a = new B; // run-time error, implicit casts are disabled
explicit A a = cast(A) new B; // allowed again
(the above could be "undefined" in spec - the responsibility is now the
user's not the compiler's)

and now opCmp becomes:
opCmp(explicit A a1, Explicit A a2);

if (b < c) ... // no opCmp matches for these types

Note: explicit violation cannot be a compile-time error because the
compiler cannot check all cases, for example:
A b = new B;
A c = new C;
...code...
opCmp(b,c);  // b and c have static type A
the above will throw at run-time

Questions remaining:
a) is this a generally useful to have in D, or should it be limited
(maybe with a special syntax just for opCmp) ?
b) in order to ensure a compile-time error instead of a run-time
exception the compiler needs to know if the static type matches the
dynamic one, so, Should there be additional rules and restrictions
(similar to constancy ) in order for the compiler to enforce this attribute?

_My_personal_conclusion_:  I think that D really needs a facility like
Java annotations. If D had such a thing, than a library could define
such an attribute without adding bloat to the language (a new keyword)
and without changing the compiler. It probably would be very simple to
provide such a solution.

Another way to solve this problem is with reflection:
int opCmp(A a1, A a2) {
    if (cast(A) a1 is null || cast(A) a2 is null) throw new
ComparisonException;
    .... perform comparison ...
}

The second solution can work today, but I'd prefer to tag the parameters
with an "explicit" annotation and have some library code do the above
test in the beginning of opCmp instead of doing it myself every time.
It'll also provide a more readable code IMO.

--Yigal



More information about the Digitalmars-d mailing list