Swift - Compare generics when equatable - swift

I need to compare a generic parameter when it's comparable, something like this:
class MyClass<Item> {
var items: [[Items]]? {
didSet {
if Item is Equatable {
print(oldValue == items)
} else {
print(false)
}
}
}
}
How can I achieve this?
I've also tried:
extension TableViewModel where Item: Equatable {
func f() -> Bool {
return true
}
}
extension TableViewModel {
func f() -> Bool {
return false
}
}
and adding an f() call to my didSet.
just to see if I can get the f() function from the Equatable extension to get called (had no success).

Without beeing able to see your classes etc, i was able to produce what you want. You need to make sure the equatable methods are used properly however.
class Item {
}
class Items {
}
class Item2 : Item, Equatable {
static func ==(_ lhs: Item2,_ rhs: Item2) -> Bool{
return true
}
static func !=(_ lhs: Item2,_ rhs: Item2) -> Bool{
return true
}
}
class MyClass<Item> {
var items: [[Items]]? {
didSet {
}
}
}
extension MyClass where Item : Equatable {
func f() {
print("Hello")
}
}
class TheClass : MyClass<Item2> {
func someFunc() {
super.f()
}
}
let it = TheClass()
it.f() //prints : "Hello"
You just had to go an extra sub-classing step, by applying Equatable to the subclasses of Item (for those you wanted subclassed in the first place).
Explanation:
you wanted a class which if subclassed for a generic Equatable type, could perform the method f(). This being said, you delegated Item as your top level type for conditionnally checking Equatable. That means since Item can or cannot be Equatable, you can only apply the protocol to a subclass of Item. If you applied Equatable straight to Item, you wouldn't have your problem. So to demonstrate this, i subclassed Item into Item2, and added Equatable to Item2. Thus creating a possibility for Item to exist in Equatable form.
Now you bring in MyClass<Item>, which is a seperate entity altogether, but which can adopt a type of class which inherits from or is Item altogether. But, since you specify that you only want subclasses of Item which are Equatable (ie: class Item2), you need to extend MyClass conditionaly for the types of Item which are Equatable as well. Hence the extension.
So finally, to demonstrate the whole subclassing logic, you need to subclass MyClass<Item>.. AGAIN.. but this time, by specifying this subclass to take Item2 as a generic type in order to explicitely declare that this subclass is equatable, because the only sub-generic Item type which is equatable is Item2 in your case.
Just by re-reading myself, it can be confusing.. But what you need to remember is that by applying generics, and by conforming them to protocols by CONDITIONAL EXTENSION, you allow for several different types of that same generic class to exist. Therefore, it increases the number of subclassing needed in order to adopt certain methods, in specific use-cases.
Also. As for your items : [[Item]]? didSet{} variable. I'm looking into it.. Howver i've found in this post a way to make it work with Equatable in Swift 4. I'm updating to Swift 4 as we speak to test out my code.

Related

How do I check the class of an instance that conforms to a protocol in swift?

I'm trying to check the class of an instance which conforms to a protocol.
I have a protocol.
protocol ToolbarProtocol {
func show()
func hide()
}
I have a class which conforms to that protocol.
class GameToolbar: ToolbarProtocol {
...
}
I have a manager class I createed to manage my toolbars.
class ToolbarManager {
var existingToolbars: [Game.rotation: Array<ToolbarProtocol>]
}
In this manager, I have a function that wants to find the first instance of a specific type of toolbar.
func getDebugToolbar() -> ToolbarProtocol? {
return existingToolbars[.east]?.first(where: { (toolbar: ToolbarProtocol) -> Bool in
toolbar.isKind(of: GameToolbar.self) //This line causes an error because .isKind is not a member of ToolbarProtocol
})
}
I can't call isKind(of) on toolbar, which previously worked when my toolbars were a different kind of class provided by an external library (which I'm trying to remove from my codebase because I want different functionality).
I tried making my protocol extend AnyObject, but I think that's implicit anyway, and it had no effect.
How can I check an array of instances which conform to a given protocl, to check for specific class types?
I think you would need to attempt to cast it, like
if let vc = toolbar as? GameToolbar {}
In your case, you might need something like this:
func getDebugToolbar() -> ToolbarProtocol? {
return existingToolbars[.east]?.first(where: { (toolbar: ToolbarProtocol) -> Bool in
let _ = toolbar as? GameToolbar
})
}

Swift protocols and equatable

I'm still learning how to work with arrays of objects implementing protocols with associated types.
I have the following protocols:
public protocol Word : Equatable, Hashable { // compiles
associatedtype WordType : Equatable
var moreWords: [WordType] { get }
}
public protocol WordDataSource { // compiles
associatedtype SomeWord : Word
func findWord(spelling: String) -> SomeWord?
}
I have WordA, WordB and WordC all implementing Word and subclassing NSObject
Basically, I want to implement the datasource protocol using different kinds of class implementing the Word class. This is the kind of code I would like to write, but obviously it doesn't compile.
class MyDataSource : WordDataSource {
func findWord(spelling: String) -> SomeWord? {
if conditionA {
return WordA()
}
if conditionB {
return WordB()
}
if conditionA {
return WordC()
}
}
}
Is that even possible in Swift? What should I write to make that work?
Thanks a lot for your help!
This is not possible, and it's not possible for a reason. Let's assume that your class MyDataSource does compile. Now, we could write such code:
let fooWord = MyDataSource().findWord(spelling: "Foo") // Would return WordA instance
let barWord = MyDataSource().findWord(spelling: "Bar") // Would return WordB instance
but all we know about those two types is that they are of this SomeWord type. So they should be comparable, since Word is comparable, right?
But they're two completely different types, so how would you know how should they be compared? Take a look at the definition of Equatable protocol:
public static func ==(lhs: Self, rhs: Self) -> Bool
You can only compare two objects of the same type that conform to this protocol.

Using diff in an array of objects that conform to a protocol

I'm experimenting with using Composition instead of Inheritance and I wanted to use diff on an array of objects that comply with a given protocol.
To do so, I implemented a protocol and made it comply with Equatable:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field:Equatable {
var content: String { get }
}
func ==<T: Field>(lhs: T, rhs: T) -> Bool {
return lhs.content == rhs.content
}
func ==<T: Field, U: Field>(lhs: T, rhs: U) -> Bool {
return lhs.content == rhs.content
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")] // đź’Ą boom
But I've soon discovered that:
error: protocol 'Field' can only be used as a generic constraint because it has Self or associated type requirements
I understand why since Swift is a type-safe language that needs to be able to know the concrete type of these objects at anytime.
After tinkering around, I ended up removing Equatable from the protocol and overloading the == operator:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field {
var content: String { get }
}
func ==(lhs: Field, rhs: Field) -> Bool {
return lhs.content == rhs.content
}
func ==(lhs: [Field], rhs: [Field]) -> Bool {
return (lhs.count == rhs.count) && (zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })) // naive, but let's go with it for the sake of the argument
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
// Requirement #1: direct object comparison
print(First(content: "abc") == First(content: "abc")) // true
print(First(content: "abc") == Second(content: "abc")) // false
// Requirement #2: being able to diff an array of objects complying with the Field protocol
let array1:[Field] = [First(content: "abc"), Second(content: "abc")]
let array2:[Field] = [Second(content: "abc")]
print(array1 == array2) // false
let outcome = array1.diff(array2) // đź’Ą boom
error: value of type '[Field]' has no member 'diff'
From here on, I'm a bit lost to be honest. I read some great posts about type erasure but even the provided examples suffered from the same issue (which I assume is the lack of conformance to Equatable).
Am I right? And if so, how can this be done?
UPDATE:
I had to stop this experiment for a while and totally forgot about a dependency, sorry! Diff is a method provided by SwiftLCS, an implementation of the longest common subsequence (LCS) algorithm.
TL;DR:
The Field protocol needs to comply with Equatable but so far I have not been able to do this. I need to be able to create an array of objects that comply to this protocol (see the error in the first code block).
Thanks again
The problem comes from a combination of the meaning of the Equatable protocol and Swift’s support for type overloaded functions.
Let’s take a look at the Equatable protocol:
protocol Equatable
{
static func ==(Self, Self) -> Bool
}
What does this mean? Well it’s important to understand what “equatable” actually means in the context of Swift. “Equatable” is a trait of a structure or class that make it so that any instance of that structure or class can be compared for equality with any other instance of that structure or class. It says nothing about comparing it for equality with an instance of a different class or structure.
Think about it. Int and String are both types that are Equatable. 13 == 13 and "meredith" == "meredith". But does 13 == "meredith"?
The Equatable protocol only cares about when both things to be compared are of the same type. It says nothing about what happens when the two things are of different types. That’s why both arguments in the definition of ==(::) are of type Self.
Let’s look at what happened in your example.
protocol Field:Equatable
{
var content:String { get }
}
func ==<T:Field>(lhs:T, rhs:T) -> Bool
{
return lhs.content == rhs.content
}
func ==<T:Field, U:Field>(lhs:T, rhs:U) -> Bool
{
return lhs.content == rhs.content
}
You provided two overloads for the == operator. But only the first one has to do with Equatable conformance. The second overload is the one that gets applied when you do
First(content: "abc") == Second(content: "abc")
which has nothing to do with the Equatable protocol.
Here’s a point of confusion. Equability across instances of the same type is a lower requirement than equability across instances of different types when we’re talking about individually bound instances of types you want to test for equality. (Since we can assume both things being tested are of the same type.)
However, when we make an array of things that conform to Equatable, this is a higher requirement than making an array of things that can be tested for equality, since what you are saying is that every item in the array can be compared as if they were both of the same type. But since your structs are of different types, you can’t guarantee this, and so the code fails to compile.
Here’s another way to think of it.
Protocols without associated type requirements, and protocols with associated type requirements are really two different animals. Protocols without Self basically look and behave like types. Protocols with Self are traits that types themselves conform to. In essence, they go “up a level”, like a type of type. (Related in concept to metatypes.)
That’s why it makes no sense to write something like this:
let array:[Equatable] = [5, "a", false]
You can write this:
let array:[Int] = [5, 6, 7]
or this:
let array:[String] = ["a", "b", "c"]
or this:
let array:[Bool] = [false, true, false]
Because Int, String, and Bool are types. Equatable isn’t a type, it’s a type of a type.
It would make “sense” to write something like this…
let array:[Equatable] = [Int.self, String.self, Bool.self]
though this is really stretching the bounds of type-safe programming and so Swift doesn’t allow this. You’d need a fully flexible metatyping system like Python’s to express an idea like that.
So how do we solve your problem? Well, first of all realize that the only reason it makes sense to apply SwiftLCS on your array is because, at some level, all of your array elements can be reduced to an array of keys that are all of the same Equatable type. In this case, it’s String, since you can get an array keys:[String] by doing [Field](...).map{ $0.content }. Perhaps if we redesigned SwiftLCS, this would make a better interface for it.
However, since we can only compare our array of Fields directly, we need to make sure they can all be upcast to the same type, and the way to do that is with inheritance.
class Field:Equatable
{
let content:String
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.content == rhs.content
}
init(_ content:String)
{
self.content = content
}
}
class First:Field
{
init(content:String)
{
super.init(content)
}
}
class Second:Field
{
init(content:String)
{
super.init(content)
}
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")]
The array then upcasts them all to type Field which is Equatable.
By the way, ironically, the “protocol-oriented” solution to this problem actually still involves inheritance. The SwiftLCS API would provide a protocol like
protocol LCSElement
{
associatedtype Key:Equatable
var key:Key { get }
}
We would specialize it with a superclass
class Field:LCSElement
{
let key:String // <- this is what specializes Key to a concrete type
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.key == rhs.key
}
init(_ key:String)
{
self.key = key
}
}
and the library would use it as
func LCS<T: LCSElement>(array:[T])
{
array[0].key == array[1].key
...
}
Protocols and Inheritance are not opposites or substitutes for one another. They complement each other.
I know this is probably now what you want but the only way I know how to make it work is to introduce additional wrapper class:
struct FieldEquatableWrapper: Equatable {
let wrapped: Field
public static func ==(lhs: FieldEquatableWrapper, rhs: FieldEquatableWrapper) -> Bool {
return lhs.wrapped.content == rhs.wrapped.content
}
public static func diff(_ coll: [Field], _ otherCollection: [Field]) -> Diff<Int> {
let w1 = coll.map({ FieldEquatableWrapper(wrapped: $0) })
let w2 = otherCollection.map({ FieldEquatableWrapper(wrapped: $0) })
return w1.diff(w2)
}
}
and then you can do
let outcome = FieldEquatableWrapper.diff(array1, array2)
I don't think you can make Field to conform to Equatable at all as it is designed to be "type-safe" using Self pseudo-class. And this is one reason for the wrapper class. Unfortunately there seems to be one more issue that I don't know how to fix: I can't put this "wrapped" diff into Collection or Array extension and still make it support heterogenous [Field] array without compilation error:
using 'Field' as a concrete type conforming to protocol 'Field' is not supported
If anyone knows a better solution, I'm interested as well.
P.S.
In the question you mention that
print(First(content: "abc") == Second(content: "abc")) // false
but I expect that to be true given the way you defined your == operator

A way to inherit from multiple classes

I have two classes I want to use in my new class. The first one implements a swipe to delete and the second enables a long press gesture:
class DeleteItem: UITableViewCell {
}
class OpenDetail: UITableViewCell {
}
Since Swift doesn't allow a class to inherit from multiple classes the following example obviously won't work:
class ItemViewCell: DeleteItem, OpenDetail {
}
So in order to create ItemViewCell and having both options, I'll have to have one of the classes to inherit from each other:
class DeleteItem: UITableViewCell {
}
class OpenDetail: DeleteItem {
}
class ItemViewCell: OpenDetail {
}
The problem is, if I only want the long press gesture I'll have to create a new class without inheriting from DeleteItem. Is there a better way of doing this?
This is the perfect case for using Protocols and Protocol extension. A swift protocol is like an interface in Java for example. A protocol can define a set of functions which has to be implemented by the entities which want to conform to this protocol, moreover a protocol can define properties which has to be present in these entities too. For example:
protocol ItemDeleter {
var deletedCount: Int {get set}
func deleteItem(item: ItemType)
}
The problem is, that each entity would have to provide its own implementation of func deleteItem(item: ItemType) even if multiple entities share the same logic of deleting an item, this where a protocol extension comes in handy. For example:
extension ItemDeleter {
func deleteItem(item: ItemType) {
// logic needed to delete an item
// maybe incremented deletedCount too
deletedCount++
}
}
Then you could make your ItemViewCell conform to the ItemDeleter protocol, in this case all you need is to make sure that ItemViewCell has a property deletedCount: Int. It does not need to provide an implementation for func deleteItem(item: ItemType) as the protocol itself provides a default implementation for this function, however you can override it in your class, and the new implementation will be used. The same applies for DetailOpener protocol.

Circular dependencies between generic types (CollectionType and its Index/Generator, e.g.)

Given a struct-based generic CollectionType …
struct MyCollection<Element>: CollectionType, MyProtocol {
typealias Index = MyIndex<MyCollection>
subscript(i: Index) -> Element { … }
func generate() -> IndexingGenerator<MyCollection> {
return IndexingGenerator(self)
}
}
… how would one define an Index for it …
struct MyIndex<Collection: MyProtocol>: BidirectionalIndexType {
func predecessor() -> MyIndex { … }
func successor() -> MyIndex { … }
}
… without introducing a dependency cycle of death?
The generic nature of MyIndex is necessary because:
It should work with any type of MyProtocol.
MyProtocol references Self and thus can only be used as a type constraint.
If there were forward declarations (à la Objective-C) I would just[sic!] add one for MyIndex<MyCollection> to my MyCollection<…>. Alas, there is no such thing.
A possible concrete use case would be binary trees, such as:
indirect enum BinaryTree<Element>: CollectionType, BinaryTreeType {
typealias Index = BinaryTreeIndex<BinaryTree>
case Nil
case Node(BinaryTree, Element, BinaryTree)
subscript(i: Index) -> Element { … }
}
Which would require a stack-based Index:
struct BinaryTreeIndex<BinaryTree: BinaryTreeType>: BidirectionalIndexType {
let stack: [BinaryTree]
func predecessor() -> BinaryTreeIndex { … }
func successor() -> BinaryTreeIndex { … }
}
One cannot (yet?) nest structs inside generic structs in Swift.
Otherwise I'd just move BinaryTreeIndex<…> inside BinaryTree<…>.
Also I'd prefer to have one generic BinaryTreeIndex,
which'd then work with any type of BinaryTreeType.
You cannot nest structs inside structs because they are value types. They aren’t pointers to an object, instead they hold their properties right there in the variable. Think about if a struct contained itself, what would its memory layout look like?
Forward declarations work in Objective-C because they are then used as pointers. This is why the indirect keyword was added to enums - it tells the compiler to add a level of indirection via a pointer.
In theory the same keyword could be added to structs, but it wouldn’t make much sense. You could do what indirect does by hand instead though, with a class box:
// turns any type T into a reference type
final class Box<T> {
let unbox: T
init(_ x: T) { unbox = x }
}
You could the use this to box up a struct to create, e.g., a linked list:
struct ListNode<T> {
var box: Box<(element: T, next: ListNode<T>)>?
func cons(x: T) -> ListNode<T> {
return ListNode(node: Box(element: x, next: self))
}
init() { box = nil }
init(node: Box<(element: T, next: ListNode<T>)>?)
{ box = node }
}
let nodes = ListNode().cons(1).cons(2).cons(3)
nodes.box?.unbox.element // first element
nodes.box?.unbox.next.box?.unbox.element // second element
You could turn this node directly into a collection, by conforming it to both ForwardIndexType and CollectionType, but this isn’t a good idea.
For example, they need very different implementations of ==:
the index needs to know if two indices from the same list are at the same position. It does not need the elements to conform to Equatable.
The collection needs to compare two different collections to see if they hold the same elements. It does need the elements to conform to Equatable i.e.:
func == <T where T: Equatable>(lhs: List<T>, rhs: List<T>) -> Bool {
// once the List conforms to at least SequenceType:
return lhs.elementsEqual(rhs)
}
Better to wrap it in two specific types. This is “free” – the wrappers have no overhead, just help you build the right behaviours more easily:
struct ListIndex<T>: ForwardIndexType {
let node: ListNode<T>
func successor() -> ListIndex<T> {
guard let next = node.box?.unbox.next
else { fatalError("attempt to advance past end") }
return ListIndex(node: next)
}
}
func == <T>(lhs: ListIndex<T>, rhs: ListIndex<T>) -> Bool {
switch (lhs.node.box, rhs.node.box) {
case (nil,nil): return true
case (_?,nil),(nil,_?): return false
case let (x,y): return x === y
}
}
struct List<T>: CollectionType {
typealias Index = ListIndex<T>
var startIndex: Index
var endIndex: Index { return ListIndex(node: ListNode()) }
subscript(idx: Index) -> T {
guard let element = idx.node.box?.unbox.element
else { fatalError("index out of bounds") }
return element
}
}
(no need to implement generate() – you get an indexing generator “for free” in 2.0 by implementing CollectionType)
You now have a fully functioning collection:
// in practice you would add methods to List such as
// conforming to ArrayLiteralConvertible or init from
// another sequence
let list = List(startIndex: ListIndex(node: nodes))
list.first // 3
for x in list { print(x) } // prints 3 2 1
Now all of this code looks pretty disgusting for two reasons.
One is because box gets in the way, and indirect is much better as the compiler sorts it all out for you under the hood. But it’s doing something similar.
The other is that structs are not a good solution to this. Enums are much better. In fact the code is really using an enum – that’s what Optional is. Only instead of nil (i.e. Optional.None), it would be better to have a End case for the end of the linked list. This is what we are using it for.
For more of this kind of stuff you could check out these posts.
While Airspeed Velocity's answer applies to the most common cases, my question was asking specifically about the special case of generalizing CollectionType indexing in order to be able to share a single Index implementation for all thinkable kinds of binary trees (whose recursive nature makes it necessary to make use of a stack for index-based traversals (at least for trees without a parent pointer)), which requires the Index to be specialized on the actual BinaryTree, not the Element.
The way I solved this problem was to rename MyCollection to MyCollectionStorage, revoke its CollectionType conformity and wrap it with a struct that now takes its place as MyCollection and deals with conforming to CollectionType.
To make things a bit more "real" I will refer to:
MyCollection<E> as SortedSet<E>
MyCollectionStorage<E> as BinaryTree<E>
MyIndex<T> as BinaryTreeIndex<T>
So without further ado:
struct SortedSet<Element>: CollectionType {
typealias Tree = BinaryTree<Element>
typealias Index = BinaryTreeIndex<Tree>
subscript(i: Index) -> Element { … }
func generate() -> IndexingGenerator<SortedSet> {
return IndexingGenerator(self)
}
}
struct BinaryTree<Element>: BinaryTreeType {
}
struct BinaryTreeIndex<BinaryTree: BinaryTreeType>: BidirectionalIndexType {
func predecessor() -> BinaryTreeIndex { … }
func successor() -> BinaryTreeIndex { … }
}
This way the dependency graph turns from a directed cyclic graph into a directed acyclic graph.