I'm using Xcode version 11.4.1 (11E503a) on a Catalina system, with a playground.
extension Sequence {
/// Returns the first corresponding pair of elements from this sequence and
/// the given sequence that are not equivalent, using the given predicate as
/// the equivalence test.
public func firstDelta<S: Sequence>(
against other: S,
by areEquivalent: (Element, S.Element) throws -> Bool
) rethrows -> (Element?, S.Element?) {
var iterator = makeIterator(), otherIterator = other.makeIterator()
while true {
let first = iterator.next(), second = otherIterator.next()
if let f = first, let s = second, try areEquivalent(f, s) {
continue
} else {
return (first, second)
}
}
}
func elementsEqual2<OtherSequence: Sequence>(
_ other: OtherSequence,
by areEquivalent: (Element, OtherSequence.Element) throws -> Bool
) rethrows -> Bool {
return try firstDelta(against: other, by: areEquivalent) == (nil, nil) // HERE
}
}
The error is on the "==" of the indicated line:
Type '(Self.Element?, OtherSequence.Element?)' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols
I know we're working on this, but it shouldn't matter here, since the already provided top-level functions should cover it, and we're not checking for conformance.
Wouldn't you need a constraint that Element and OtherSequence.Element also conform to Equatable to be able to choose the == <T: Equatable, U: Equatable> (lhs: (T, U), rhs: (T, U)) variant?
Adapting your solution worked, but that isn't the real problem.
I forgot that any Optional can be equality-compared to nil, even if the Wrapped type isn't Equatable. There is a custom == operator for nil comparisons. But a custom operator outside of the Equatable network isn't transitive; I guess the current way of tuple per-member equality check uses Equatable conformance, and not any existing == that is accessible.
So I had to separate the members before comparing:
That’s right, there are specific == (and !=) overloads for tuples with up to six Equatable members. If you want to be able to compare tuples directly to (nil, nil, ...), you’d need to define your own overloads:
public func == <A, B>(lhs: (A?, B?), rhs: (_OptionalNilComparisonType, _OptionalNilComparisonType)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1
}
// Plus overloads for reversed lhs and rhs, !=, and longer tuples
But it might be simpler to just compare the elements to nil one-by-one in elementsEqual2:
I think you're making this too complex by trying to make (nil, nil) actually mean the same as a wrapping nil. Instead, how about this?
extension Sequence {
/// Returns the first corresponding pair of elements from this sequence and
/// the given sequence that are not equivalent, using the given predicate as
/// the equivalence test.
public func firstDelta<S: Sequence>(
against other: S,
by areEquivalent: (Element, S.Element) throws -> Bool
) rethrows -> (Element?, S.Element?)? {
try AnySequence( zip: (self, other) ).first {
try Optional(optionals: $0).map(areEquivalent).map(!)
?? true
}
}
func elementsEqual2<OtherSequence: Sequence>(
_ other: OtherSequence,
by areEquivalent: (Element, OtherSequence.Element) throws -> Bool
) rethrows -> Bool {
try firstDelta(against: other, by: areEquivalent) == nil
}
}
public extension AnySequence {
/// Like `zip`, but with nil elements after shorter sequences are exhausted.
init<Sequence0: Sequence, Sequence1: Sequence>(
zip zipped: (Sequence0, Sequence1)
) where Element == (Sequence0.Element?, Sequence1.Element?) {
self.init(
sequence(
state: ( zipped.0.makeIterator(), zipped.1.makeIterator() )
) { iterators in
Optional(
( iterators.0.next(), iterators.1.next() ),
nilWhen: { $0 == nil && $1 == nil }
)
}
)
}
}
public extension Optional {
/// Wraps a value in an optional, based on a condition.
/// - Parameters:
/// - wrapped: A non-optional value.
/// - getIsNil: The condition that will result in `nil`.
init(
_ wrapped: Wrapped,
nilWhen getIsNil: (Wrapped) throws -> Bool
) rethrows {
self = try getIsNil(wrapped) ? nil : wrapped
}
/// Exchange two optionals for a single optional tuple.
/// - Returns: `nil` if either tuple element is `nil`.
init<Wrapped0, Wrapped1>( optionals: (Wrapped0?, Wrapped1?) )
where Wrapped == (Wrapped0, Wrapped1) {
switch optionals {
case let (wrapped0?, wrapped1?):
self = (wrapped0, wrapped1)
default:
self = nil
}
}
}
I don't want to make that equivalence; I just wanted to compare both members to nil at once. It it's not worth it, so I just did:
I have two Optionals within a non-Optional tuple because the four states have distinct user-level meanings. Neither being nil means each sequence has at least one element after a common prefix. Only the first as nil means that the first sequence is a prefix of the second. Vice-versa goes when only the second is nil. The sequences are identical when both returns are nil. What is your fifth state (making the entire tuple nil) supposed to mean?