Object.toString, toHash, opCmp, opEquals

Jonathan M Davis newsgroup.d at jmdavisprog.com
Fri Apr 26 00:06:51 UTC 2024


On Thursday, April 25, 2024 5:06:27 PM MDT Walter Bright via Digitalmars-d 
wrote:
> The prototypes are:
>
> ```
> string toString();
> size_t toHash() @trusted nothrow;
> int opCmp(Object o);
> bool opEquals(Object o);
> ```
>
> which long predated `const`. The trouble is, they should be:
>
> ```
> string toString() const;
> size_t toHash() const @trusted nothrow;
> int opCmp(const Object o) const;
> bool opEquals(const Object o) const;
> ```
>
> Without the `const` annotations, the functions are not usable by `const`
> objects without doing an unsafe cast. This impairs anyone wanting to write
> const-correct code, and also impedes use of `@live` functions.
>
> I recommend that everyone who has overloads of these functions, alter them
> to have the `const` signatures. This will future-proof them against any
> changes to Object's signatures.

The problem with this is that D's const is not logical const, and some
objects cannot work if these functions are fully const (e.g. beacuse they're
using a mutex or because they have to lazily calculate their state). Having
_any_ attributes on these functions is a problem, because those attributes
restrict what derived classes can do. Similarly, not having any attributes
causes problems, because then they can't be used in code that requires those
attributes.

The solution to this problem (as has been discussed plenty of times in the
past) is to outright remove these functions from Object. The only reason
that they need to be there is because of code that's written to use Object
instead of using templates, and D has templates. The main blocker then has
been two things:

1. We haven't wanted to break existing code by removing these functions from
Object. Editions hopefully give us a way to move past that problem.

2. Instead of being properly templated, some of the key druntime code (e.g.
involving hash tables) has used Object. Some work has been done to fix
various parts of druntime (like the hooks for arrays) so that they're
templated, but the work has not been completed. If the various parts of
druntime which require Object are fully fixed to be templated, then we don't
need these functions on Object any longer. The code would just be
instantiated with whatever the class type that's given is, and those
functions can then have whatever attributes are appropriate for that
particular class hierarchy without Object needing to have them any more than
Object has a foobarWilly function, because some stray library needs that for
its class hierarchy.

We currently have a partial solution in druntime in that the free function,
opEquals, is templated so that if you try to use == on class references
which are not Object, it will use the derived class' version of opEquals,
thus allowing classes to define opEquals with whatever attributes are
appropriate for that particular class hierarchy (as well as allowing them to
make their opEquals take the type of that specific class instead of Object).
The problem of course is that when you compare classes as Object, you get
the Object version, but most code doesn't use Object directly, so in
general, == works with whatever attributes we want, and if we get rid of
opEquals from Object, those comparisons should still work. You then won't be
able to use == on Object, but that's a pretty nonsensical comparison anyway.
It's only ever made sense in code where you couldn't templatize it and
therefore needed a base class to use (like Java does with its containers),
and even then, for most code, it makes far more sense to use a base class
from that particular project than to use Object, in which case, that base
class can be given whatever functions or attributes are appropriate to that
particular class hierarchy.

__cmp is similarly templated, though I'm not as familiar with how the
lowerings work with regards to opCmp. But as long as all of the comparison
operators lower to templated functions that call opEquals or opCmp on the
class references with whatever type they have instead of with Object, we can
work around Object's versions of those functions.

toHash and toString would typically be called more directly, but if the code
that's calling them is templated rather than using Object, derived classes
can currently declare versions of those functions with a different set of
attributes (though since those functions don't have parameters, they're
somewhat more restricted in which attributes can be used, since they can't
overload on most attributes) - and with regards to const, they can overload
the Object versions, meaning that they only have a problem if Object is
being used.

Adding const - or any other attributes - to the functions on Object would be
a step backwards rather than forwards and needlessly restrict code. Rather,
we need to take advantage of templates and and Editions and make it so that
none of these functions are on Object at all, allowing each individual class
hierarchy to define these functions in whatever manner makes sense for that
code. Editions gives us an opportunity here which we have not had
previously, and we should take grab it.

- Jonathan M Davis





More information about the Digitalmars-d mailing list