Tuples Conform to Equatable
- Proposal: SE-NNNN
- Author: Alejandro Alonso
- Review Manager: TBD
- Status: Awaiting review
- Implementation: apple/swift#28833
Introduction
Introduce Equatable
conformance for all tuples whose elements are themselves Equatable
.
Swift-evolution thread: Tuples Conform to Equatable
This idea has been heavily discussed numerous times before with dozens of threads discussing this issue.
Motivation
Tuples in Swift currently lack the ability to conform to protocols. This has led many users to stop using tuples altogether in favor of structures that they can them conform protocols to. The shift from tuples to structures have made tuples almost feel like a second class type in the language because of them not being able to do simple operations that should just work.
Consider the following snippet of code that naively tries to use tuples for simple operations, but instead is faced with an ugly error.
let points = [(x: 128, y: 316), (x: 0, y: 0), (x: 100, y: 42)]
let origin = (x: 0, y: 0)
// error: argument type '(x: Int, y: Int)' does not conform to expected type 'Equatable'
// or with newer diagnostics
// error: type '(x: Int, y: Int)' cannot conform to 'Equatable';
// only struct/enum/class types can conform to protocols
if points.contains(origin) {
// do some serious calculations here
}
This also creates friction when one needs to conditionally conform to a type, or if a type is just trying to get free conformance synthesis for protocols like Equatable
or Hashable
.
struct Restaurant {
let name: String
let location: (latitude: Int, longitude: Int)
}
// error: type 'Restaurant' does not conform to protocol 'Equatable'
extension Restaurant: Equatable {}
These are simple and innocent examples of trying to use tuples in one's code, but currently the language lacks the means to get these examples working and prevents the user from writing this code.
After all the errors, one decides to give in and create a structure to mimic the tuple layout. From a code size perspective, creating structures to mimic each unique tuple need adds a somewhat significant amount of size to one's binary.
Proposed solution
Introduce Equatable
conformance for all tuples whose elements are Equatable
themselves. While this isn't a general purpose conform any tuple to any protocol, Equatable
is a crucial protocol to conform to because it allows for all of the snippets above in Motivation to compile and run as expected.
The rule is simple: if all of the tuple elements are themselves Equatable
then the overall tuple itself conforms to Equatable
.
// Ok, Int is Equatable thus the tuples are Equatable
(1, 2, 3) == (1, 2, 3) // true
struct EmptyStruct {}
// error: type '(EmptyStruct, Int, Int' does not conform to protocol 'Equatable'
// note: value of type 'EmptyStruct' does not conform to protocol 'Equatable',
// preventing conformance of '(EmptyStruct, Int, Int)' to 'Equatable'
(EmptyStruct(), 1, 2) == (EmptyStruct(), 1, 2)
It's also important to note that this conformance does not take into account the tuple labels in consideration for equality. If both tuples have the same types, then they can be compared for equality.
// We don't take into account the labels for equality.
(x: 0, y: 0) == (0, 0) // true
Source compatibility
These are completely new conformances to tuples, thus source compatibilty is unaffected as they were previously not Equatable
.
Effect on ABI stability
The conformance to Equatable
is additive to the ABI and requires a newer runtime to support. Thus one will need a platform, who has declared ABI stability, which embeds a Swift X (replace version here with version this feature shipped with) runtime.
Alternatives considered
Besides not doing this entirely, the only alternative here is whether or not we should hold off on this before we get proper protocol conformances for tuples which allow them to conform to any protocol. Doing this now requires a lot of builtin machinery in the compiler which some may refer to as technical debt. While I agree with this statement, I don't believe we should be holding off on features like this that many are naturally reaching for until bigger and more complex proposals that allow this feature to natively exist in Swift. I also believe it is none of the user's concern for what technical debt is added to the compiler that allows them to write the Swift code that they feel comfortable writing. In any case, the technical debt to be had here should only be the changes to the runtime which allow this feature to work.
Future Directions
With this change, other conformances such as Hashable
and Codable
would make for other great conformances for tuples. It also makes sense to implement other conformances for other structural types in the language such as metatypes, existentials, etc.
In the future when we have proper tuple extensions along with variadic generics and such, implementing Equatable
for tuples will be trivial and I imagine the standard library will come with a conformance for tuples. When that happens all future usage of that conformance will use the standard library's implementation, but older clients that have been compiled with this implementation will continue using it as normal.