Type '(Int, ())' cannot conform to 'Equatable'

After SE283, we have Equatable support for tuple so that (Int, Int) can conform to Equatable.

However since () does not conform to Equatable, we'll still be stuck here.

See real world example from OpenCombine

XCTAssertEqual(history.count, 3)
// tuples aren't Equatable, so matching the elements one by one
switch history[0] {
case .subscription("Zip"):
  break
default:
  XCTFail("Failed to match the first subscription event in \(#function)")
}

switch history[1] {
case .value(let v):
    if v.0 != 42 || v.1 != () {
        XCTFail("Failed to match the value event in \(#function)")
    }
default:
  XCTFail("Failed to match the value event in \(#function)")
}

switch history[2] {
case .completion(.finished):
    break
default:
    XCTFail("Failed to match the completion event in \(#function)")
}

If we can make () to conform to Equatable, we can just use very simple compare.

XCTAssertEqual(history, [.subscription("Zip"), .value(42, ()), .completion(.finished)])
1 Like

I don't believe SE-0283 was ever actually added to the language. There were issues with the implementation that led to its merge being reverted, and I believe at this point its acceptance is considered expired.

There is a more recent pitch for user-definable tuple conformances that would include this in the standard library, though.

According to the post, you can enable the TupleConformances upcoming feature flag to try it out. It might not be stable or fully implemented yet, though.

5 Likes

Oh yeah. I thought it was implemented because we can do "b1 == b2". But actually they are not conforming to Equatable. (Confused me: where is the == implementation :thinking:?)

public func test() {
    let a1 = (3, ())
    let a2 = (4, ())

    let b1 = (3, 3)
    let b2 = (4, 3)
    b1 == b2 // ⚠️ Result of operator '==' is unused
    e(b1, b2) // ❌ Argument type '(Int, Int)' does not conform to expected type 'Equatable'
    e(a1, a2) // ❌ Argument type '(Int, ())' does not conform to expected type 'Equatable'
}

func e(_ a: any Equatable, _ b: any Equatable) { ... }

According to the post, you can enable the TupleConformances upcoming feature flag to try it out.

It seems that it has not been integrated into main / release/5.10 of Swift. And we need a corresponding PR toolchain to test it.

2 Likes

Found this SE0015

Looks like Swift have hardcoded such comparison ability in the core library. (Supporting tuple count from 2 to 6)

2 Likes

Experimental feature flags are only available in developer toolchains, and tuple conformances are not implemented all the way yet, in fact most examples will crash once you get past type checking.

3 Likes

Not sure which file exactly, but somewhere in the standard library there are top-level functions == and != with overloads for tuples of up to six elements. I believe there’s the other comparison operators too, but I haven’t tested them yet.

You could still do that if you conform the embedding type to Equatable, right? Something like that:

extension HistoryElement: Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
            case let (.subscription(a), .subscription(b)): a == b
            case let (.completion(a), .completion(b)): a == b
            case let (.value(a, b), .value(c, d)): a == c && b == d
            default: false
        }
    }
}