Extension on Generic Type: Bug ?

Hello,


I wrote a Dictionary extension in Swift (2.0 Beta 2) that compare two Dictionaries and return a tuple with the modified, inserted and deleted values between them :


extension Dictionary where Key: Hashable, Value: Equatable {
    func compareToOld (oldDictionary:[Key:Value]) -> (deleted: [Key: Value], modifiedOldValues: [Key: Value], modifiedNewValues: [Key: Value], inserted: [Key: Value]) {
        if oldDictionary.count == 0 {
            return ([Key: Value](), [Key: Value](), [Key: Value](), self)
        }

        if self.count == 0 {
            return (oldDictionary, [Key: Value](), [Key: Value](), [Key: Value]())
        }

        var valuesToAdd = [Key: Value]()
        var valuesToModifyNewValues = [Key: Value]()
        var valuesToModifyOldValues = [Key: Value]()
        var valuesToDelete = [Key: Value]()
        for (k, v) in oldDictionary {
            if self.keys.array.contains(k) == false {
                valuesToDelete.updateValue(v, forKey: k)
            }
        }
        for (k, v) in self {
            if let obj = oldDictionary[k]   {
                if v != obj {
                    valuesToModifyNewValues.updateValue(v, forKey: k)
                    valuesToModifyOldValues.updateValue(obj, forKey: k)
                }
            } else {
                valuesToAdd.updateValue(v, forKey: k)
            }
        }

        return (valuesToDelete, valuesToModifyOldValues, valuesToModifyNewValues, valuesToAdd)
    }
}


It works perfectly with a [String: String] dictionary

var d = ["k1": "v1", "k2": "v2", "k3": "v3"]
var d1 = ["k1": "v1", "k2": "v2a", "k4": "v4"]
let r = d1.compareToOld(d)
r.deleted // Return ["k3": "v3"]
r.inserted // Return ["k4": "v4"]
r.modifiedNewValues // Return ["k2": "v2a"]
r.modifiedOldValues // Return ["k2": "v2"]


But it's not working with a [String: [String]] dictionary :

var d = ["k1": ["v1"], "k2": ["v2"], "k3": ["v3"]]
var d1 = ["k1": ["v1"], "k2": ["v2a"], "k4": ["v4"]]
let r = d1.compareToOld(d)
r.deleted
r.inserted
r.modifiedNewValues
r.modifiedOldValues


I have the following error :

cannot invoke 'compareToOld' with an argument list of type '([String : Array<String>])'

let r = d1.compareToOld(d)


Or, as we can see in these lines before, [String] is conform to Equatable :

var a1 = ["v1"]
var a2 = ["v1"]
var a3 = ["v2"]
a1 == a2 // Return true
a1 == a3 // Return false


Am I missing something ?


Thanks in advance for your feedback,


Kind Regards,


Alexis.

Accepted Answer

The generic type Array does not conform to Equatable. This can be further confirmed by eg:

extension Array: Equatable {} // Error: Type 'Array' does not conform to protocol 'Equatable'


If Array had been Equatable, we would instead have seen the same error message as the one we get for eg String (which is Equatable):

extension String: Equatable {} // Error: Redundant conformance of 'String' to protocol 'Equatable'


But why does your check "work"?

While something like the following seems to suggest that Array conforms to Equatable:

let a = ["Hello", "world"]
let b = ["Hello", "world"]
print(a == b) // Prints true, but doesn't mean Array is Equatable ...

It's not actually the case.

If we examine what is actually happening by jumping to the definition of that == in the std lib (ctrl-cmd-J on it), we'll see this:

/// Returns true if these arrays contain the same elements.
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool


Note that the T there is not the Array itself, it's just the Element type of the array type, the arrays are specified as the type of the parameters, namely [T], not just T. So that function works for Arrays whose Element type is Equatable, not for Equatable Arrays.


In order to make Arrays, with equatable elements, conform to Equatable, I suppose we would have to do something like the following (which does not work (Swift 2, beta 2)):

extension Array: Equatable where T: Equatable { } // Error: Extension of type 'Array' with constraints cannot have an inheritance clause.


I too would be interested to see a simple solution to your example, working for both Value: Equatable and Value: Array<T> where T: Equatable.

Thanks Jens! It was very helpful!


So I just have to write a specific extension for dictionary of Array<T> where T:Equatable.


Thanks again!


Alexis.

Extension on Generic Type: Bug ?
 
 
Q