> On 25 May 2016, at 12:21, Brent Royal-Gordon <br...@architechies.com> wrote: > >> Brent, I think it's even slightly more complicated than that. Think e.g. how >> two subclass instances of NSArray should compare equal even if they've got >> different types. (I really dislike class inheritance for these reasons, but >> below is my take on how we could better live with the issue in Swift.) > > If you're referring to this: > >>> guard let other = other as? Self else { >>> return super == other >>> } > > I probably should have said `#Self`, meaning e.g. `NSArray` if you're > implementing `NSArray.==`. What you really want to test for there is the > *static* type of `self`, not the dynamic type. > > If you're referring to the multiple dispatch thing, I actually think that > will handle subclassing perfectly well. If you have a class hierarchy like > this, with each class implementing its own `(Self, Self)` equality operation: > > NSObject > NSArray > MyArray > MyOtherArray > > Then using `==` on `MyArray` and `MyOtherArray` should use `(NSArray, > NSArray).==()`, which presumably would compare the length and elements to > test for equality.
It's kind of hard to explain without working code so here's a sample to play with: http://swiftlang.ng.bluemix.net/#/repl/c1ddd24113169ab82df118660c8e0de6ea24e48d32997c327638a88dc686e91f <http://swiftlang.ng.bluemix.net/#/repl/c1ddd24113169ab82df118660c8e0de6ea24e48d32997c327638a88dc686e91f>. Use the `#if true` line to toggle between the implementations. The core of the issue—which I think will also happen if we ever gain the ability to "open" existentials—is at the point where we cast the right-hand side to Self: struct AnyEquatable : Equatable { let value: Any let isEqual: (AnyEquatable) -> Bool init<T : Equatable>(_ value: T) { self.value = value self.isEqual = {r in guard let other = r.value as? T.EqualSelf else { return false } return value == other } } } func == (l: AnyEquatable, r: AnyEquatable) -> Bool { return l.isEqual(r) } See the cast `r.value as? T.EqualSelf`, or `r.value as? T` like it would go for the stdlib Equatable. When `T` is MyArray or MyOtherArray and the erased type of `r.value` is not, the cast will fail. >> For the very example of Equatable and Foundation classes, we would get the >> right behaviour for NSObject's `isEqual` by changing the definition of >> Equatable into: >> >> protocol Equatable { >> associatedtype EqualSelf = Self // the default is ok pretty much >> always >> func == (lhs: Self, rhs: EqualSelf) -> Bool >> func != (lhs: Self, rhs: EqualSelf) -> Bool >> } >> >> This way, the compiler would always be looking for the `(Self, NSObject) -> >> Bool` shape of operation, which actually picks up statically the correct >> overload for `lhs.isEqual(rhs)`. > > But you would need to do this for all operators. A protocol that requires `<` > or `+` would need to de-privilege the right-hand side in exactly the same way. That's true. Maybe if we want to take this issue into account we should name `EqualSelf` more generally. Or then we can just shrug away the problem and document it. It's not something I tend to face in real work, partly because I try to avoid inheritance as much as possible, but I know it's there. — Pyry
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution