Skip to content

Commit

Permalink
Remove collection conformances to Equatable and Hashable (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Vermeulen authored Apr 9, 2021
1 parent bcdbac8 commit abc5559
Show file tree
Hide file tree
Showing 14 changed files with 2 additions and 209 deletions.
4 changes: 2 additions & 2 deletions Guides/Compacted.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 0 additions & 3 deletions Sources/Algorithms/Chain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(_:_:)
//===----------------------------------------------------------------------===//
Expand Down
15 changes: 0 additions & 15 deletions Sources/Algorithms/Chunked.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 0 additions & 2 deletions Sources/Algorithms/Combinations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:)
Expand Down
45 changes: 0 additions & 45 deletions Sources/Algorithms/Compacted.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
3 changes: 0 additions & 3 deletions Sources/Algorithms/Cycle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 0 additions & 2 deletions Sources/Algorithms/Indexed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 0 additions & 2 deletions Sources/Algorithms/Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(_:_:)
Expand Down
18 changes: 0 additions & 18 deletions Sources/Algorithms/Stride.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
2 changes: 0 additions & 2 deletions Sources/Algorithms/Windows.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
12 changes: 0 additions & 12 deletions Tests/SwiftAlgorithmsTests/ChunkedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
37 changes: 0 additions & 37 deletions Tests/SwiftAlgorithmsTests/CompactedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)
}
}
}
}
13 changes: 0 additions & 13 deletions Tests/SwiftAlgorithmsTests/StrideTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClosedRange<Int>>.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
Expand Down
53 changes: 0 additions & 53 deletions Tests/SwiftAlgorithmsTests/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,6 @@ struct SplitMix64: RandomNumberGenerator {
}
}

// An eraser helper to any hashable sequence.
struct AnyHashableSequence<Base>
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<Self> {
AnyHashableSequence(base: self)
}
}

// An eraser helper to any mutable collection
struct AnyMutableCollection<Base> where Base: MutableCollection {
var base: Base
Expand Down Expand Up @@ -184,38 +163,6 @@ func XCTAssertEqualCollections<C1: Collection, C2: Collection>(
}
}

func hash<T: Hashable>(_ 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<T: Hashable, U: Hashable>(
_ 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<T: Hashable, U: Hashable>(
_ 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`,
Expand Down

0 comments on commit abc5559

Please sign in to comment.