ProtoObject and comparison for equality and ordering

Jonathan M Davis newsgroup.d at jmdavisprog.com
Tue May 14 22:02:58 UTC 2019


On Tuesday, May 14, 2019 1:34:12 PM MDT Andrei Alexandrescu via Digitalmars-
d wrote:
> In designing ProtoObject and comparison for equality and ordering, we've
> assumed all class objects are supposed to be comparable (including
> ProtoObject themselves). That means code like this should always compile:
>
> bool fun(C, D)(C x, D y) if (is(C == class) && is(D == class))
> {
>     return x < y && x == y;
> }
>
> That is, any two class objects should be comparable for equality (==,
> !=) and ordering (<. >, <=, >=). The decision whether comparison
> actually works for the types involved is deferred to runtime.
>
> This is in keeping with Java, C#, and existing D where Object has
> built-in means for comparison.
>
> However the question Jonathan M Davis asked got me thinking - perhaps we
> should break with tradition and opt for a more statically-checked means
> of comparison. The drawback is that some objects would NOT be
> comparable, which may surprise some users.
>
> As a consequence, for example, creating hash tables keyed on certain
> types will not work. This is not quite unheard of as a type could
> disable opEquals. Also, by default struct types cannot be compared for
> ordering - they must define opCmp.
>
> Should we go with a more statically-checked/imposed approach with
> comparison, or stick with OOP tradition? Ideas welcome.

Well, I think that my stance on it is pretty clear at this point. Languages
like Java and C# had to put these functions on Object, because they didn't
have templates and thus had to have containers and the like contain Object
rather than the actual type. D does not have that problem.

By templatizing the core infrastructure such as the free function opEquals
that == gets lowered to for classes, any class that defined the appropriate
function could then work with whatever attributes were on it (basically like
we have now for structs). As soon as a class defined opEquals or toString or
whatever, any classes derived from that class would then be restricted by
the attribute choices on the base class, but only that class hierarchy would
then have to live with that decision rather than the entire language like
what we have now with Object or what was shown with interfaces in Eduard's
talk. So, each class hierarchy could use whichever set of attributes made
sense for it.

All we'd really lose that I can see is the ability to compare classes from
disparate hierarchies, which has never really worked properly or made much
sense anyway. You just end up with unrelated objects not being considered
equal instead of statically knowing that you're comparing objects that
aren't really logically comparable. The full dynamic capabilities of
inheritance and polymorphism are still there within class hierarchies. They
just aren't at the root object level and thus would require casting to more
specific types to use them. And by not having them at the root object level,
we avoid the problem of locking in a particular set of attributes.

I think that all of this fits in quite well with how we've already been
discussing templatizing more of druntime so that it's pay as you go. The
only serious implementation issue that I'm aware of would be the built-in
AAs, since there would be no common base class or interface with opEquals or
toHash (meaning that at least for now, only Object could be put in the
built-in AAs and not classes that aren't derived from Object). But that
would be fixed by templatizing the AA implementation, which is already
something that we've wanted to do. IIRC, Martin Nowak was working on
something along those lines previously, but I don't know where that work
currently stands.

- Jonathan M Davis





More information about the Digitalmars-d mailing list