Skip to content

Remove collection conformances to Equatable and Hashable #124

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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