The question is how you want AnyEquatable
to actually implement the ==
operator.
Standard library currently has two very different erasing wrappers over types that conform to Equatable
(or an inherited protocol): one being AnyHashable
, as you mention, but there's also AnyIndex
, which abstracts Comparable
.
There is a good reason to have AnyHashable
as is, because it is able to work "standalone": a hashable value can produce its hash regardless of its Self
constraint, and here the fact that Hashable
inherits from Equatable
is a more semantical constraint than anything inherently required for hashing to work. If you would try to compare two AnyHashable
s with different underlying types, it will gracefully return false
.
On the other hand, AnyIndex
is an abstraction to be able to work with collections regardless of their index types, but indices can't really do anything as "standalone" values: they are tied to startIndex
and endIndex
of a collection, for example, and such still need to be able to compare as their underlying types. Because of this, comparing two unrelated AnyIndex
es is a semantic disaster, so it will actually crash at runtime if you'd try to.
So the question is, how should AnyEquatable
actually behave when trying to equate two underlying values? On the one hand, it seems reasonable that AnyEquatable(5) == AnyEquatable("5")
should return false
— that is, you just assume that diffrent types are incomparable — but should AnyEquatable(5 as Int) == AnyEquatable(5 as Int8)
return false? Or should these crash?
More generally, I beleive there already have been some discussions on enabling the compiler to sythesize Any<Whatever>
wrappers over protocols with Self
and stuff, so it might be a much more generic question of whether protocols should be granted this ability and what their default runtime behaviour should be.