From abc5559ef6f0be29f1ba67e024b8defcd963df5a Mon Sep 17 00:00:00 2001 From: Tim Vermeulen Date: Fri, 9 Apr 2021 21:44:16 +0200 Subject: [PATCH] Remove collection conformances to `Equatable` and `Hashable` (#124) --- Guides/Compacted.md | 4 +- Sources/Algorithms/Chain.swift | 3 -- Sources/Algorithms/Chunked.swift | 15 ------ Sources/Algorithms/Combinations.swift | 2 - Sources/Algorithms/Compacted.swift | 45 ---------------- Sources/Algorithms/Cycle.swift | 3 -- Sources/Algorithms/Indexed.swift | 2 - Sources/Algorithms/Product.swift | 2 - Sources/Algorithms/Stride.swift | 18 ------- Sources/Algorithms/Windows.swift | 2 - Tests/SwiftAlgorithmsTests/ChunkedTests.swift | 12 ----- .../SwiftAlgorithmsTests/CompactedTests.swift | 37 ------------- Tests/SwiftAlgorithmsTests/StrideTests.swift | 13 ----- .../SwiftAlgorithmsTests/TestUtilities.swift | 53 ------------------- 14 files changed, 2 insertions(+), 209 deletions(-) diff --git a/Guides/Compacted.md b/Guides/Compacted.md index 2e361839..17ef8d4f 100644 --- a/Guides/Compacted.md +++ b/Guides/Compacted.md @@ -31,8 +31,8 @@ extension Collection { ``` One is a more general `CompactedSequence` for any `Sequence` base. And the other a more specialized `CompactedCollection` -where base is a `Collection` and with conditional conformance to `BidirectionalCollection`, `RandomAccessCollection`, -`LazyCollectionProtocol`, `Equatable` and `Hashable` when base collection conforms to them. +where base is a `Collection` and with conditional conformance to `BidirectionalCollection`, `RandomAccessCollection` and +`LazyCollectionProtocol` when base collection conforms to them. ### Naming diff --git a/Sources/Algorithms/Chain.swift b/Sources/Algorithms/Chain.swift index 844cbfa9..57dee3a4 100644 --- a/Sources/Algorithms/Chain.swift +++ b/Sources/Algorithms/Chain.swift @@ -283,9 +283,6 @@ extension Chain2: BidirectionalCollection extension Chain2: RandomAccessCollection where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} -extension Chain2: Equatable where Base1: Equatable, Base2: Equatable {} -extension Chain2: Hashable where Base1: Hashable, Base2: Hashable {} - //===----------------------------------------------------------------------===// // chain(_:_:) //===----------------------------------------------------------------------===// diff --git a/Sources/Algorithms/Chunked.swift b/Sources/Algorithms/Chunked.swift index 12573590..66f6bf57 100644 --- a/Sources/Algorithms/Chunked.swift +++ b/Sources/Algorithms/Chunked.swift @@ -512,21 +512,6 @@ extension Collection { } } -// Conditional conformances. -extension ChunkedByCount: Equatable where Base: Equatable {} - -// Since we have another stored property of type `Index` on the -// collection, synthesis of `Hashble` conformace would require -// a `Base.Index: Hashable` constraint, so we implement the hasher -// only in terms of `base` and `chunkCount`. Since the computed -// index is based on it, it should not make a difference here. -extension ChunkedByCount: Hashable where Base: Hashable { - @inlinable - public func hash(into hasher: inout Hasher) { - hasher.combine(base) - hasher.combine(chunkCount) - } -} extension ChunkedByCount.Index: Hashable where Base.Index: Hashable {} // Lazy conditional conformance. diff --git a/Sources/Algorithms/Combinations.swift b/Sources/Algorithms/Combinations.swift index ebc28baa..7b4a4219 100644 --- a/Sources/Algorithms/Combinations.swift +++ b/Sources/Algorithms/Combinations.swift @@ -181,8 +181,6 @@ extension Combinations: Sequence { } extension Combinations: LazySequenceProtocol where Base: LazySequenceProtocol {} -extension Combinations: Equatable where Base: Equatable {} -extension Combinations: Hashable where Base: Hashable {} //===----------------------------------------------------------------------===// // combinations(ofCount:) diff --git a/Sources/Algorithms/Compacted.swift b/Sources/Algorithms/Compacted.swift index 304287d2..a2070201 100644 --- a/Sources/Algorithms/Compacted.swift +++ b/Sources/Algorithms/Compacted.swift @@ -179,48 +179,3 @@ extension CompactedCollection: LazySequenceProtocol where Base: LazySequenceProtocol {} extension CompactedCollection: LazyCollectionProtocol where Base: LazyCollectionProtocol {} - - -// Hashable and Equatable conformance are based on each non-nil -// element on base collection. -extension CompactedSequence: Equatable - where Base.Element: Equatable { - - @inlinable - public static func ==(lhs: CompactedSequence, - rhs: CompactedSequence) -> Bool { - lhs.elementsEqual(rhs) - } -} - -extension CompactedSequence: Hashable - where Element: Hashable { - - @inlinable - public func hash(into hasher: inout Hasher) { - for element in self { - hasher.combine(element) - } - } -} - -extension CompactedCollection: Equatable - where Base.Element: Equatable { - - @inlinable - public static func ==(lhs: CompactedCollection, - rhs: CompactedCollection) -> Bool { - lhs.elementsEqual(rhs) - } -} - -extension CompactedCollection: Hashable - where Element: Hashable { - - @inlinable - public func hash(into hasher: inout Hasher) { - for element in self { - hasher.combine(element) - } - } -} diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 975211fb..fc6671df 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -163,9 +163,6 @@ extension FiniteCycle: BidirectionalCollection extension FiniteCycle: RandomAccessCollection where Base: RandomAccessCollection {} -extension FiniteCycle: Equatable where Base: Equatable {} -extension FiniteCycle: Hashable where Base: Hashable {} - //===----------------------------------------------------------------------===// // cycled() //===----------------------------------------------------------------------===// diff --git a/Sources/Algorithms/Indexed.swift b/Sources/Algorithms/Indexed.swift index 19fbcbc9..d1fdd378 100644 --- a/Sources/Algorithms/Indexed.swift +++ b/Sources/Algorithms/Indexed.swift @@ -72,8 +72,6 @@ extension Indexed: BidirectionalCollection where Base: BidirectionalCollection { extension Indexed: RandomAccessCollection where Base: RandomAccessCollection {} extension Indexed: LazySequenceProtocol where Base: LazySequenceProtocol {} extension Indexed: LazyCollectionProtocol where Base: LazyCollectionProtocol {} -extension Indexed: Equatable where Base: Equatable {} -extension Indexed: Hashable where Base: Hashable {} //===----------------------------------------------------------------------===// // indexed() diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index 73017901..dafb9932 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -438,8 +438,6 @@ extension Product2: RandomAccessCollection where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} extension Product2.Index: Hashable where Base1.Index: Hashable, Base2.Index: Hashable {} -extension Product2: Equatable where Base1: Equatable, Base2: Equatable {} -extension Product2: Hashable where Base1: Hashable, Base2: Hashable {} //===----------------------------------------------------------------------===// // product(_:_:) diff --git a/Sources/Algorithms/Stride.swift b/Sources/Algorithms/Stride.swift index 347825b8..f81f1707 100644 --- a/Sources/Algorithms/Stride.swift +++ b/Sources/Algorithms/Stride.swift @@ -269,22 +269,4 @@ extension StrideCollection: BidirectionalCollection } extension StrideCollection: RandomAccessCollection where Base: RandomAccessCollection {} - -extension StrideCollection: Equatable where Base.Element: Equatable { - @inlinable - public static func == (lhs: StrideCollection, rhs: StrideCollection) -> Bool { - lhs.elementsEqual(rhs, by: ==) - } -} - -extension StrideCollection: Hashable where Base.Element: Hashable { - @inlinable - public func hash(into hasher: inout Hasher) { - hasher.combine(stride) - for element in self { - hasher.combine(element) - } - } -} - extension StrideCollection.Index: Hashable where Base.Index: Hashable {} diff --git a/Sources/Algorithms/Windows.swift b/Sources/Algorithms/Windows.swift index 336881b1..6d8e269a 100644 --- a/Sources/Algorithms/Windows.swift +++ b/Sources/Algorithms/Windows.swift @@ -350,6 +350,4 @@ extension Windows: BidirectionalCollection where Base: BidirectionalCollection { extension Windows: LazySequenceProtocol where Base: LazySequenceProtocol {} extension Windows: LazyCollectionProtocol where Base: LazyCollectionProtocol {} extension Windows: RandomAccessCollection where Base: RandomAccessCollection {} -extension Windows: Equatable where Base: Equatable {} -extension Windows: Hashable where Base: Hashable, Base.Index: Hashable {} extension Windows.Index: Hashable where Base.Index: Hashable {} diff --git a/Tests/SwiftAlgorithmsTests/ChunkedTests.swift b/Tests/SwiftAlgorithmsTests/ChunkedTests.swift index aa78a7ac..10fcc136 100644 --- a/Tests/SwiftAlgorithmsTests/ChunkedTests.swift +++ b/Tests/SwiftAlgorithmsTests/ChunkedTests.swift @@ -145,16 +145,4 @@ final class ChunkedTests: XCTestCase { validateIndexTraversals(chunks) } } - - func testChunksOfCountHash() { - let collection1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - let collection2 = [1, 2, 3, 4, 5] - - XCTAssertEqualHashValue( - collection1.chunks(ofCount: 1), collection1.chunks(ofCount: 1)) - XCTAssertNotEqualHashValue( - collection1.chunks(ofCount: 1), collection2.chunks(ofCount: 1)) - XCTAssertNotEqualHashValue( - collection1.chunks(ofCount: 2), collection2.chunks(ofCount: 4)) - } } diff --git a/Tests/SwiftAlgorithmsTests/CompactedTests.swift b/Tests/SwiftAlgorithmsTests/CompactedTests.swift index 476d5bba..dbff888f 100644 --- a/Tests/SwiftAlgorithmsTests/CompactedTests.swift +++ b/Tests/SwiftAlgorithmsTests/CompactedTests.swift @@ -41,41 +41,4 @@ final class CompactedTests: XCTestCase { validateIndexTraversals(array.compacted()) } } - - func testCollectionEquatableConformances() { - for array in self.tests { - XCTAssertEqual( - array.eraseToAnyHashableSequence().compacted(), - array.compactMap({ $0 }).eraseToAnyHashableSequence().compacted() - ) - XCTAssertEqual( - array.compacted(), array.compactMap({ $0 }).compacted() - ) - } - } - - func testCollectionHashableConformances() { - for array1 in self.tests { - for array2 in self.tests { - // For non-equal Collections and Sequences that produce the same - // compacted, the compacted wrapper should produce the same hash. - // e.g. [1, 2, 3, nil, nil, 4].compacted() should produce the - // same hash as [1, nil, 2, nil, 3, 4].compacted() - guard !array1.elementsEqual(array2) && - array1.compacted() == array2.compacted() else { - continue - } - - let seq = array1.eraseToAnyHashableSequence() - let seq2 = array2.eraseToAnyHashableSequence() - - XCTAssertEqualHashValue( - seq.compacted(), seq2.compacted() - ) - XCTAssertEqualHashValue( - array1.compacted(), array2.compacted() - ) - } - } - } } diff --git a/Tests/SwiftAlgorithmsTests/StrideTests.swift b/Tests/SwiftAlgorithmsTests/StrideTests.swift index c452ddd3..6143a466 100644 --- a/Tests/SwiftAlgorithmsTests/StrideTests.swift +++ b/Tests/SwiftAlgorithmsTests/StrideTests.swift @@ -64,19 +64,6 @@ final class StridingTests: XCTestCase { // a.index(after: a.endIndex) // Precondition failed: Advancing past end index } - func testStrideCompositionEquivalence() { - let a = (0...10) - XCTAssertEqualSequences(a.striding(by: 6), a.striding(by: 2).striding(by: 3)) - XCTAssertTrue(a.striding(by: 6) == a.striding(by: 2).striding(by: 3)) - XCTAssert(type(of: a.striding(by: 2).striding(by: 3)) == StrideCollection>.self) - } - - func testEquality() { - let a = [1, 2, 3, 4, 5].striding(by: 2) - let b = [1, 0, 3, 0, 5].striding(by: 2) - XCTAssertEqual(a, b) - } - func testStrideLast() { XCTAssertEqual((1...10).striding(by: 2).last, 9) // 1, 3, 5, 7, 9 XCTAssertEqual((1...10).striding(by: 3).last, 10) // 1, 4, 7, 10 diff --git a/Tests/SwiftAlgorithmsTests/TestUtilities.swift b/Tests/SwiftAlgorithmsTests/TestUtilities.swift index 26f005a3..6322a997 100644 --- a/Tests/SwiftAlgorithmsTests/TestUtilities.swift +++ b/Tests/SwiftAlgorithmsTests/TestUtilities.swift @@ -46,27 +46,6 @@ struct SplitMix64: RandomNumberGenerator { } } -// An eraser helper to any hashable sequence. -struct AnyHashableSequence - where Base: Sequence, Base: Hashable { - var base: Base -} - -extension AnyHashableSequence: Hashable {} -extension AnyHashableSequence: Sequence { - typealias Iterator = Base.Iterator - - func makeIterator() -> Iterator { - base.makeIterator() - } -} - -extension Sequence where Self: Hashable { - func eraseToAnyHashableSequence() -> AnyHashableSequence { - AnyHashableSequence(base: self) - } -} - // An eraser helper to any mutable collection struct AnyMutableCollection where Base: MutableCollection { var base: Base @@ -184,38 +163,6 @@ func XCTAssertEqualCollections( } } -func hash(_ value: T) -> Int { - var hasher = Hasher() - value.hash(into: &hasher) - return hasher.finalize() -} - -/// Asserts that two hashable instances produce the same hash value. -func XCTAssertEqualHashValue( - _ expression1: @autoclosure () throws -> T, - _ expression2: @autoclosure () throws -> U, - _ message: @autoclosure () -> String = "", - file: StaticString = #file, line: UInt = #line -) { - XCTAssertEqual( - hash(try expression1()), hash(try expression2()), - message(), file: file, line: line - ) -} - -/// Asserts that two hashable instances don't produce the same hash value. -func XCTAssertNotEqualHashValue( - _ expression1: @autoclosure () throws -> T, - _ expression2: @autoclosure () throws -> U, - _ message: @autoclosure () -> String = "", - file: StaticString = #file, line: UInt = #line -) { - XCTAssertNotEqual( - hash(try expression1()), hash(try expression2()), - message(), file: file, line: line - ) -} - /// Tests that all index traversal methods behave as expected. /// /// Verifies the correctness of the implementations of `startIndex`, `endIndex`,