Add AnyEquatable to standard library

The standard library already includes a AnyHashable that can be very useful. I propose adding a AnyEquatable type.

The implementation of this would be very similar to the AnyHashable type since the Hashable conformance already requires that type to conforms too Equatable.

Like AnyHashable I believe it would be good to add some connivance methods to some collection types where Element == AnyEquatable.

3 Likes

Do you mean any equatable ?

1 Like

What are the use cases you have in mind for such a type?

1 Like

It used to be useful, before casting broke a few versions ago. They'll have to fix that before it can be worthwhile again.

1 Like

Mostly for building any-type Equatable Collections. Since Equatable protocol has a Self reference you can't do Array<Equatable>().

A concrete example can be found in my KeyWindow package. So that it can make use of the SwiftUI's Preferences api it needs to ensure the Preference value is Equatable (otherwise we are unable to read this value higher up). This api needs to collect multiple preference values when reducing over child views. However it is not using a single concrete type so the collection needs to use a any-type however the collection also needs to be equatable hence why this package currently contains its own AnyEquatable implementation derived from the AnyHashable that can be found in the standard library.

This is not exactly a semantical constraint. Hashing is useless without the possibility to check equality of values with the same hash.

And the fact that it gracefully returns false for different types is an unfortunate limitation, as it prevents Heterogeneous lookup.

1 Like

I only want it if it's generic, à la AnySequence and its derivatives. See my implementation above for what used to work.

Right now, that means that it would have to be the same as AnyHashable: two instances that can't both be cast to a known type must be "not equal". But that's only because Equatable is old and didn't get updated for Swift 2. Equatable.== should be a throwing function.

/// An `Equatable` instance is stored as a "`Cast`".
/// Only instances that can be cast to that type can be `==`'d with the `AnyEquatable`.
public struct AnyEquatable<Cast> {

Interesting idea, so Cast would need to conform too Equatable? That is different from AnySequence were they is no type constraint on the Element type. Requiring the element type to conform to Equatable implicitly means you can only put a concrete type there. You can't put a Protocol (due to the limitations of swifts type system)..

I would suggest keep AnyEquatable as a generic free type and like AnyHashable add a protocol that you can optionally conform to that is used in the constructor of AnyEquatable (where the E: HasCustomAnyEquatableRepresentation ) that exposes the needed down-cast values for comparison.

Have you attempted to use the _HasCustomAnyHashableRepresentation protocol for AnyHashable? The exact implementation used by AnyHashable for this to me seems a little strange since the AnyHashableBox protocol is not public so you can't create a AnyHashable using a custom box type all you can do is cast the wrapped value. In addition rather than using a where H: _HasCustomAnyHashableRepresentation not he constructor this is does using a cast operation in the general constructor (maybe doe to the game of AnyHashable?).


edit: see KeyWindowTests/AnyEquatableTests.swift as an example of how this could work.

As I bug-reported (linked above), this only became true recently. It used to work just fine. I had tests. Now I'm XCTSkipping them until things work properly again.