A fresh look at comparisons

Henning Hasemann hhasemann at web.de
Mon Apr 14 07:10:58 PDT 2008


"Janice Caron" <caron800 at googlemail.com> wrote:
> On 14/04/2008, Henning Hasemann <hhasemann at web.de> wrote:
> >                         // Objects are simply incomparable
> >                         // (like (1 - i) and (i - 1) for example)
> >                         return ComparsionResult.UNDEFINED;
> 
> This is worth spending a bit of time discussing. The fact is that, in
> D, that particular comparison is not undefined. In fact, it is /very
> well/ defined. It is defined as follows:
> (snip)

Yeah, maybe undefined was simply the wrong word. What I mean is "there
is no meaningful order between the two" ie "it is neither '<' nor '>'
nor '=='" ie "the ORDER between these two is not defined" (which
is something you can't express with the current opCmp)

Just as it is with (1 - i) and (i - 1) whatever the correct
term for that would be. (I think UNORDERED would be way better)

I just wanted to distinguish that from UNKNOWN which would allow the
compiler to search for an alternative form.

Here for clarity definitions of what I meant with the two terms in my
previous posting:

ComparsionResult.UNKNOWN:
  The comparsion function does not make a
  statement wether the two objects are equal or a is greater b or
  whatever. Think of it as the comparsion function is simply not
  defined this way. For example if the compiler would ask an object
  a for "a > b" and a would return ComparsionResult.UNKNOWN The compiler
  can and should ask b for "b < a".
  If the compiler doesn't find any rewriting of the term that returns
  something else than UNKNOWN, every comparsion yields false.
  (Ie the same as if one comparsion would have returned
  UNDEFINED/UNORDERED)

  This would normally not assume well-ordering (ie the compiler would
  *not* check for "a !<= b". One could imagine a special flag like
  suggested for the is-thingy)

ComparsionResult.UNDEFINED/ComparsionResult.UNORDERED:
  If a is asked for a for being "<", ">", "<=", ">=" or "==" b, false is
  returned. Ie there is no order between the two objects.
  Naturally this implies that all the comparsion operators starting
  with "!" return true.

Implications of this approach in contrast to the is-Approach:
- As said, no special syntax except maybe for the well-ordered flag
- This version *enforces* that two objects a and b can only be in one
  of the following 4 states:
  * a is greater than b
  * b is greater than a (same as a is lower than b)
  * a is equal to b
  * a and be are unordered to each other
  This is clearly a limitation (eg a and b can not be == and < at the
  same time). I guess it depends strongly on what you want to do with
  your operators if this can be considered a good thing.
- You can inherit comparsion which might be considered useful,
  especially if your super class compares private members and you want
  to refer to super class equality:

  class A {
    int x,y;
    ComparsionResult opCompare(A other) {
      // We only care for equality
      if(x == other.x and y == other.y)
        return ComparsionResult.EQUAL;
      return ComparsionResult.UNORDERED;
    }
  }

  class B : A {
    int z;
    ComparsionResult opCompare(B other) {
      if(super.opCompare(other) == ComparsionResult.EQUAL && z ==
other.z) {
        return ComparsionResult.EQUAL;
      }
      return ComparsionResult.UNORDERED;
    }
  }
- The is() approach has a much nicer syntax and its much more easy to
  write cleary what you want
- This approach secretely assumes some magic in that the parameter to
  opCompare would always be of the same type as the class.
  (comparsion with other objects would be UNORDERED).
  The is approach addresses this better in so far that its clearly
  visible something special is going on here and that its NOT a regular
  method.


To your argument:
"""
One of the reasons that I believe comparison is "special", like
constructors, is because of the following:

    class A
    {
        int opEquals(Object o)
        {
           ...
        }

        int opCmp(Object o)
        {
           ...
        }
    }

    class B :A
    {
        int n;
    }

Hopefully, you can see the problem here immediately. B extends A, but
B does not provide opEquals nor opCmp, so if two Bs are compared, A's
compare functions will be called, which means that B's member n will
be ignored.

This is, of course, absolutely normal behavior for classes. It's how
inheritance works. But it's just not right for comparisons.
""":

I don't think this is bad or even would feel somehow incorrect.
Depending on what type of code you write you don't want every member
automagically being compared. In some game code I write curretly there
are for example lots of members which would not apply for comparsion.
I think its more a matter of taste if members should be compared by
default or not.
But with throwing away inheritance you throw away all the
considerations you made in A which properties identify two equal
objects. Is that really what you want?


So enough for the war for now ;)
I really like your idea just to make myself clear.
(And except for a few points it seems to be the better way especially
regarding clarity)
I just had this idea
popped up when I read yours and wanted to fully discuss it. Hereby
done.

Henning

-- 
GPG Public Key:
http://gpg-keyserver.de/pks/lookup?op=get&search=0xDDD6D36D41911851




More information about the Digitalmars-d mailing list