Overriding equals with generic class - swift

I created a class which is meant to be used as an "abstract class" (only to be subclassed, not instantiated directly). Since Swift doesn't support this, is has to be emulated using e.g. fatalError in body of abstract method.
My abstract class has to be equatable. So I thought, I use fatalError in equals method:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==(lhs: MySubClass, rhs: MySubClass) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass(1)
let b = MySubClass(2)
let c = MySubClass(2)
a == b
b == c
And this works. I have the little problem though that my subclass has a type parameter. Now the example looks like this:
class MySuperClass:Equatable {
}
func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
fatalError("Must override")
}
class MySubClass<T>:MySuperClass {
let id:Int
init(_ id:Int) {
self.id = id
}
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
And now it crashes, because it doesn't "see" the overriding equals, and it executes only equals in the superclass.
I know Swift has some issues concerning overrides using generic types. I thought though that this was limited to interaction with obj-c. This looks at least like a language deficiency, or bug, why would equals of generic class B not override equals of class A, if B is a subclass of A?

As Airspeed suggests the problem is that operator's implementation is not a part of class/struct implementation => hence, inheritance does not work there.
What you can do is to keep the logic inside of the class implementation and make operators use it. E.g. the following will do what you need:
class MySuperClass: Equatable {
func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
fatalError("Must override")
}
}
func == (lhs: MySuperClass, rhs: MySuperClass) -> Bool {
return lhs.isEqualTo(rhs)
}
class MySubClass<T>:MySuperClass {
let id: Int
init(_ id: Int) {
self.id = id
}
override func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
if let anotherSubClass = anotherSuperClass as? MySubClass<T> {
return self.id == anotherSubClass.id
}
return super.isEqualTo(anotherSuperClass) // Updated after AirSpeed remark
}
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
a == b
b == c
... as you can see == operator is defined only once and it uses MySuperClass's method to figure out whether its two arguments are equal. And after that .isEqualTo() handles the rest, including the use of inheritance mechanism on MySubClass level.
UPD
The benefit of above approach is that the following will still work:
let a2: MySuperClass = a
let b2: MySuperClass = b
let c2: MySuperClass = c
a2 == b2
b2 == c2
... i.e. regardless of the variable type at compile-time the behaviour will be determined by the actual instance type.

In overloading resolution, non-generic functions always have priority over generic ones, so the lesser rule that functions taking subclasses precede ones taking superclasses isn't taken into account.
A possible solution is to make the superclass == generic too. That way the rules for choosing between two generic functions kick in, and in this case the more specific one is the one taking specific classes parameterized by T:
func ==<T: MySuperClass>(lhs: T, rhs: T) -> Bool {
// bear in mind, this is a question of compile-time overloading,
// rather than overriding
fatalError("Must override")
}
func ==<T>(lhs: MySubClass<T>, rhs: MySubClass<T>) -> Bool {
return lhs.id == rhs.id
}
let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)
// no longer asserts
a == b
b == c

Related

Why Swift Tuples can be compared only when the number of elements is less than or equal to 6?

I read in HackingWithSwift that Swift tuples can be compared with == operator only when the number of elements is less than or equal to 6. What is the reason behind this limitation ?
Background: Tuples aren't Equatable
Swift's tuples aren't Equatable, and they actually can't be (for now). It's impossible to write something like:
extension (T1, T2): Equatable { // Invalid
// ...
}
This is because Swift's tuples are structural types: Their identity is derived from their structure. Your (Int, String) is the same as my (Int, String).
You can contrast this from nominal types, whose identity is solely based off their name (well, and the name of the module that defines them), and whose structure is irrelevant. An enum E1 { case a, b } is different from an enum E2 { case a, b }, despite being structurally equivalent.
In Swift, only nominal types can conform to protocols (like Equatble), which precludes tuples from being able to participate.
...but == operators exist
Despite this, == operators for comparing tuples are provided by the standard library. (But remember, since there is still no conformance to Equatable, you can't pass a tuple to a function where an Equatable type is expected, e.g. func f<T: Equatable>(input: T).)
One == operator has to be manually be defined for every tuple arity, like:
public func == <A: Equatable, B: Equatable, >(lhs: (A,B ), rhs: (A,B )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, >(lhs: (A,B,C ), rhs: (A,B,C )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, >(lhs: (A,B,C,D ), rhs: (A,B,C,D )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, >(lhs: (A,B,C,D,E ), rhs: (A,B,C,D,E )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool { ... }
Of course, this would be really tedious to write-out by hand. Instead, it's written using GYB ("Generate your Boilerplate"), a light-weight Python templating tool. It allows the library authors to implement == using just:
% for arity in range(2,7):
% typeParams = [chr(ord("A") + i) for i in range(arity)]
% tupleT = "({})".format(",".join(typeParams))
% equatableTypeParams = ", ".join(["{}: Equatable".format(c) for c in typeParams])
// ...
#inlinable // trivial-implementation
public func == <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
${", ".join("lhs.{}".format(i) for i in range(1, arity))}
) == (
${", ".join("rhs.{}".format(i) for i in range(1, arity))}
)
}
Which then gets expanded out by GYB to:
#inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable>(lhs: (A,B), rhs: (A,B)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1
) == (
rhs.1
)
}
#inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2
) == (
rhs.1, rhs.2
)
}
#inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable>(lhs: (A,B,C,D), rhs: (A,B,C,D)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3
) == (
rhs.1, rhs.2, rhs.3
)
}
#inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable>(lhs: (A,B,C,D,E), rhs: (A,B,C,D,E)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4
) == (
rhs.1, rhs.2, rhs.3, rhs.4
)
}
#inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4, lhs.5
) == (
rhs.1, rhs.2, rhs.3, rhs.4, rhs.5
)
}
Even though they automated this boilerplate and could theoretically change for arity in range(2,7): to for arity in range(2,999):, there is still a cost: All of these implementations have to be compiled and produce machine code that ends up bloating the standard library. Thus, there's still a need for a cutoff. The library authors chose 6, though I don't know how they settled on that number in particular.
Future
There's two ways this might improve in the future:
There is a Swift Evolution pitch (not yet implemented, so there's no official proposal yet) to introduce Variadic generics, which explicitly mentions this as one of the motivating examples:
Finally, tuples have always held a special place in the Swift language, but working with arbitrary tuples remains a challenge today. In particular, there is no way to extend tuples, and so clients like the Swift Standard Library must take a similarly boilerplate-heavy approach and define special overloads at each arity for the comparison operators. There, the Standard Library chooses to artificially limit its overload set to tuples of length between 2 and 7, with each additional overload placing ever more strain on the type checker. Of particular note: This proposal lays the ground work for non-nominal conformances, but syntax for such conformances are out of scope.
This proposed language feature would allow one to write:
public func == <T...>(lhs: T..., rhs: T...) where T: Equatable -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}
Which would be a general-purpose == operator that can handle tuples or any arity.
There is also interest in potentially supporting non-nominal conformances, allowing structural types like Tuples to conform to protocols (like Equatable).
That would allow one to something like:
extension<T...> (T...): Equatable where T: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}
}

Contradictory protocol conformances

Can anybody explain please, what is going on here:
struct Test {
var value: Int
}
// -----------------------------------
protocol Test1: Equatable {
var value: Int { get set }
}
extension Test1 {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value + 1
}
}
// -----------------------------------
protocol Test2: Equatable {
var value: Int { get set }
}
extension Test2 {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value + 2
}
}
// -----------------------------------
extension Test: Test1 {}
extension Test: Test2 {}
let a = Test(value: 5)
let b = Test(value: 5)
print(a == b) // true, which is unexpected
If conforms only to Test1 or only to Test2 it works as expected.
Conforming to Test1 and Test2. At first I thought that the order will matter. But looks like it just cancels each other! Without any warnings. Which is very counterintuitive.
Note: Test conforms to Equatable not because of the two extensions you provided, but because of the auto-generated Equatable implementation. As you said, if there were only those two extensions, it would be ambiguous which == is the Equatable implementation.
Neither of the == in the Test1 or Test2 protocol extensions are called. Instead, the auto-generated Equatable implementation is called. As you may recall, a == operator is auto-generated for types whose properties are all Equatable, and is declared to conform to Equatable itself.
This is because members declared in extensions use static binding, so when there are multiple versions of the same member available, the version declared in the extension will only be chosen if the compile time type is the extension's type. For example:
protocol P { }
class C : P { func f() { print("C") } }
extension P { func f() { print("P") } }
C().f() // C
(C() as P).f() // P
In a == b, both a and b are Test, so none of the extension operators gets chosen. However, since Test1 and Test2 both use Self, you can only use them as generic constraints, and can't cast to them either. Therefore, I don't think you will be able to call the == declared in the extensions at all.
Anyway, if you want to see an error message saying that there are duplicate == operators available:
struct Test {
var value: Int
var x: Any? // now there is no auto-generated Equatable implementation!
}
error: type 'Test' does not conform to protocol 'Equatable' extension
Test: Test1 {} ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool {
^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool {
^
If you remove one of the extensions, then Test conforms to Equatable because of the extension, as there is no more ambiguity. Therefore, the auto-generated implementation isn't auto-generated anymore, and there is only one == to choose from - the one declared in the extension.

Swift protocol extension where Self: Equatable doesn't work

Can anyone shed a light as to why this doesn't work? I get an error Binary operator '==' cannot be applied to operands of type 'Self' and 'CustomEquatable'
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return self == other
}
}
Let's start with your CustomEquatable protocol, without the extension:
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
Let's define some types to use for experiments:
struct A: Equatable {
let name: String
}
struct B: Equatable {
let id: Int
}
Suppose we then want A and B to conform to CustomEquatable. Then we have four cases to consider:
What does a1.isEqualTo(a2) mean (where a1 and a2 are both of type A)?
What does b1.isEqualTo(b2) mean (where b1 and b2 are both of type B)?
What does a.isEqualTo(b) mean (where a is an A and b is a B)?
What does b.isEqualTo(a) mean (where b is a B and a is an A)?
For the first two cases, possible answers are that a1.isEqualTo(a2) if and only if a1 == a2 and b1.isEqualTo(b2) if and only if b1 == b2.
For the second two cases, we have to decide if there's a way for an A to equal a B. The simplest solution (I think) is that an A can never equal a B.
So we can write the conformances like this:
extension A: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? A) == self
}
}
extension B: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? B) == self
}
}
The only difference in these two conformances is the cast-to type (on the right side of as?). So we can factor out the conformances into a protocol extension like this:
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? Self) == self
}
}
With this protocol extension, we can make A and B conform to CustomEquatable without implementing isEqualTo for each:
extension A: CustomEquatable { }
extension B: CustomEquatable { }
To test the code:
let a1 = A(name: "a1")
let a2 = A(name: "a2")
let b1 = B(id: 1)
let b2 = B(id: 2)
a1.isEqualTo(a1) // true
a1.isEqualTo(a2) // false
b1.isEqualTo(b1) // true
b1.isEqualTo(b2) // false
a1.isEqualTo(b1) // false
b1.isEqualTo(a1) // false
Please watch WWDC 2015 Protocol-Oriented Programming in Swift from 37:25
This is almost literally taken from the video. You have to conditional downcast other to Self.
If it's the same type you can use == otherwise both objects are not equal anyway.
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
if let other = other as? Self { return self == other }
return false
}
}

How can I implement `hashValue` using an object's memory location? [duplicate]

Dictionary key requires Hashable conformance:
class Test {}
var dictionary = [Test: String]() // Type 'Test' dies not conform to protocol 'Hashable'
class Test: NSObject {}
var dictionary = [Test: String]() // Works
How to get address of pure Swift class instance to use as hashValue?
Equality can be implemented as object identity, i.e. a == b iff a and b refer to the same instance of the class, and the hash value can be build from the ObjectIdentifier (which is the same for identical objects, compare e.g. Difference between using ObjectIdentifier() and '===' Operator):
For Swift 4.2 and later:
class Test : Hashable {
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
For Swift 3:
class Test : Hashable {
var hashValue: Int { return ObjectIdentifier(self).hashValue }
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
For Swift 2.3 and earlier, you can use
/// Return an UnsafePointer to the storage used for `object`. There's
/// not much you can do with this other than use it to identify the
/// object
func unsafeAddressOf(object: AnyObject) -> UnsafePointer<Void>
i.e.
class Test : Hashable {
var hashValue: Int { return unsafeAddressOf(self).hashValue }
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
Example:
var dictionary = [Test: String]()
let a = Test()
let b = Test()
dictionary[a] = "A"
print(dictionary[a]) // Optional("A")
print(dictionary[b]) // nil
implement the Equatable protocol.
Swift 3
This based on the great code snippet in Martin R's answer with insightful comment from Christopher Swasey
class Test: Hashable, Equatable {
lazy var hashValue: Int = ObjectIdentifier(self).hashValue
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
}
var dictionary = [Test: String]()
let a = Test()
let b = Test()
dictionary[a] = "A"
print(dictionary[a]) // Optional("A")
print(dictionary[b]) // nil
If you don't want or cannot implement Hashable for some reason it's easy to use an Objective C helper:
(long )getPtr:(SomeType* )ptr { return (long )ptr; }
long maps to Swift Int and can be perfectly used as a Swift Dictionary key on both 32 and 64bit architectures. It was the fastest solution which I found while profiling different approaches, including unsafeAddress. In my case performance was the main criteria.

Reference as key in swift dictionary

Dictionary key requires Hashable conformance:
class Test {}
var dictionary = [Test: String]() // Type 'Test' dies not conform to protocol 'Hashable'
class Test: NSObject {}
var dictionary = [Test: String]() // Works
How to get address of pure Swift class instance to use as hashValue?
Equality can be implemented as object identity, i.e. a == b iff a and b refer to the same instance of the class, and the hash value can be build from the ObjectIdentifier (which is the same for identical objects, compare e.g. Difference between using ObjectIdentifier() and '===' Operator):
For Swift 4.2 and later:
class Test : Hashable {
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
For Swift 3:
class Test : Hashable {
var hashValue: Int { return ObjectIdentifier(self).hashValue }
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
For Swift 2.3 and earlier, you can use
/// Return an UnsafePointer to the storage used for `object`. There's
/// not much you can do with this other than use it to identify the
/// object
func unsafeAddressOf(object: AnyObject) -> UnsafePointer<Void>
i.e.
class Test : Hashable {
var hashValue: Int { return unsafeAddressOf(self).hashValue }
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
Example:
var dictionary = [Test: String]()
let a = Test()
let b = Test()
dictionary[a] = "A"
print(dictionary[a]) // Optional("A")
print(dictionary[b]) // nil
implement the Equatable protocol.
Swift 3
This based on the great code snippet in Martin R's answer with insightful comment from Christopher Swasey
class Test: Hashable, Equatable {
lazy var hashValue: Int = ObjectIdentifier(self).hashValue
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs === rhs
}
}
var dictionary = [Test: String]()
let a = Test()
let b = Test()
dictionary[a] = "A"
print(dictionary[a]) // Optional("A")
print(dictionary[b]) // nil
If you don't want or cannot implement Hashable for some reason it's easy to use an Objective C helper:
(long )getPtr:(SomeType* )ptr { return (long )ptr; }
long maps to Swift Int and can be perfectly used as a Swift Dictionary key on both 32 and 64bit architectures. It was the fastest solution which I found while profiling different approaches, including unsafeAddress. In my case performance was the main criteria.