is this a defect in equatable for swift tuples?


(David Baraff) #1

Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?


(Henrique Valcanaia) #2

If you take a look on the equal operator for tuples, you will see that both elements of the tuple must conform to Equatable.

public func ==<A, B>(lhs: (A, B), rhs: (A, B)) -> Bool where A : Equatable, B : Equatable

Your SomeClass doesn’t conforme to Equatable so it doesn’t work. If you just add the conformance it’ll work.

class SomeClass { }

extension SomeClass: Equatable {
    static func ==(_ lhs: SomeClass, _ rhs: SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let t1 = ("abc", c1)
let t2 = ("abc", c1)
t1 == t2 // legal
// true

Regards,
Henrique Valcanaia
Co-founder | iOS Engineer @ Darkshine.io <http://darkshine.io/>
Computer Engineering undergraduate student @ Inf UFRGS
http://bit.ly/valcanaia

···

On 9 Jul 2017, at 12:11, David Baraff via swift-users <swift-users@swift.org> wrote:

Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Adrian Zubarev) #3

Easy: class SomeClass : Equatable {

···

--
Adrian Zubarev
Sent with Airmail

Am 9. Juli 2017 um 17:11:14, David Baraff via swift-users (swift-users@swift.org) schrieb:

Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
return lhs === rhs
}
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jens Persson) #4

Making SomeClass conform to Equatable should fix it:

class SomeClass : Equatable {

    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {

        return lhs === rhs

    }

}

/Jens

···

On Sun, Jul 9, 2017 at 5:11 PM, David Baraff via swift-users < swift-users@swift.org> wrote:

Given 2-tuples of type (T1, T2), you should be able to invoke the ==
operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Martin R) #5

The == operator is defined for tuples with (up to 6) elements that conform to the Equatable protocol
(and < for tuples with Comparable elements):

    class SomeClass: Equatable {
        static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
            return lhs === rhs
        }
    }

    let c1 = SomeClass()
    let c2 = SomeClass()

    let t1 = ("abc", c1)
    let t2 = ("abc", c2)

    c1 == c2 // legal
    t1 == t2 // legal

The existence of a == operator alone, without declaring protocol conformance, is not sufficient.

···

On 9. Jul 2017, at 17:11, David Baraff via swift-users <swift-users@swift.org> wrote:

Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(David Baraff) #6

Easy: class SomeClass : Equatable {

Doh!!

thank you.

···

On Jul 9, 2017, at 8:16 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 9. Juli 2017 um 17:11:14, David Baraff via swift-users (swift-users@swift.org <mailto:swift-users@swift.org>) schrieb:

Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jens Persson) #7

(Also, note that your implementation of == uses lhs === rhs thus will only
return true when lhs and rhs are the same instance of SomeClass.)
/Jens

···

On Sun, Jul 9, 2017 at 5:24 PM, Jens Persson <jens@bitcycle.com> wrote:

Making SomeClass conform to Equatable should fix it:

class SomeClass : Equatable {

    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {

        return lhs === rhs

    }

}

/Jens

On Sun, Jul 9, 2017 at 5:11 PM, David Baraff via swift-users < > swift-users@swift.org> wrote:

Given 2-tuples of type (T1, T2), you should be able to invoke the ==
operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(David Baraff) #8

(Also, note that your implementation of == uses lhs === rhs thus will only return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}

I.e. since now that tuples of (Int, Tree) can be compared, I thought, great, I get an incredibly elegant description: just compare the child arrays. But this doesn’t compile, and I assume that telling the compiler that (T,Tree) is a tuple that conforms to Equatable is just not happening in this lifetime?

Not that it’s a big deal: I can of course write

  static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData && lhs.children.count == rhs.children.count &&
           all(lhs.children.indices.map { lhs.children[$0] == rhs.children[$0] }
  }

But still: it would be nice to not have to break it down manually. (all() is a free function doing what you would guess it does. i gave up resisting the urge and defined both any() and all(), after years of loving them in Python. I know I should just a more swift-like idiom, but dang it, it’s just too short. I would really love for any() and all() to become standard lib free functions. they’re so incredibly useful.)

···

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com> wrote:

/Jens

On Sun, Jul 9, 2017 at 5:24 PM, Jens Persson <jens@bitcycle.com <mailto:jens@bitcycle.com>> wrote:
Making SomeClass conform to Equatable should fix it:
class SomeClass : Equatable {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}
/Jens

On Sun, Jul 9, 2017 at 5:11 PM, David Baraff via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
Given 2-tuples of type (T1, T2), you should be able to invoke the == operator if you could on both types T1 and T2, right? i.e.

(“abc”, 3) == (“abc”, 4) // legal

but:

class SomeClass {
    static public func ==(_ lhs:SomeClass, _ rhs:SomeClass) -> Bool {
        return lhs === rhs
    }
}

let c1 = SomeClass()
let c2 = SomeClass()

let t1 = ("abc", c1)
let t2 = ("abc", c2)

c1 == c2 // legal
t1 == t2 // illegal

Why is t1 == t2 not legal given that c1 == c2 IS legal?

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(David Sweeris) #9

Right, the `==` func is *defined* for 2-element tuples where both elements conform to `Equatable`, but that tuple type doesn't itself *conform* to `Equatable`. So the`==` func that's defined on "Array where Element: Equatable" can't see it.

We'd need both "conditional conformance" and "tuple conformance" in order for that to Just Work.

- Dave Sweeris

···

On Jul 9, 2017, at 10:06, David Baraff via swift-users <swift-users@swift.org> wrote:

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com> wrote:

(Also, note that your implementation of == uses lhs === rhs thus will only return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}


(Jens Persson) #10

Since Array has .elementsEqual, another workaround (until conditional
conformance) is:

class Tree : Equatable {
    let rootData:Int
    let children:[(String, Tree)]

    init(rootData: Int, children: [(String, Tree)]) {
        self.rootData = rootData
        self.children = children
    }
    static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
        return lhs.rootData == rhs.rootData &&
            lhs.children.elementsEqual(rhs.children, by: { (a: (String,
Tree), b: (String, Tree)) -> Bool in
                return a.0 == b.0 && a.1 == b.1
            })
    }
}

···

On Sun, Jul 9, 2017 at 8:44 PM, David Sweeris <davesweeris@mac.com> wrote:

On Jul 9, 2017, at 10:06, David Baraff via swift-users < > swift-users@swift.org> wrote:

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com> wrote:

(Also, note that your implementation of == uses lhs === rhs thus will only
return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a
tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}

Right, the `==` func is *defined* for 2-element tuples where both elements
conform to `Equatable`, but that tuple type doesn't itself *conform* to
`Equatable`. So the`==` func that's defined on "Array where Element:
Equatable" can't see it.

We'd need both "conditional conformance" and "tuple conformance" in order
for that to Just Work.

- Dave Sweeris


(Martin R) #11

Since Array has .elementsEqual, another workaround (until conditional conformance) is:

class Tree : Equatable {
    let rootData:Int
    let children:[(String, Tree)]
    
    init(rootData: Int, children: [(String, Tree)]) {
        self.rootData = rootData
        self.children = children
    }
    static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
        return lhs.rootData == rhs.rootData &&
            lhs.children.elementsEqual(rhs.children, by: { (a: (String, Tree), b: (String, Tree)) -> Bool in
                return a.0 == b.0 && a.1 == b.1
            })
    }
}

Slightly simpler (since == is already defined for the tuples):

    class Tree : Equatable {
        let rootData:Int = 0
        let children:[(String, Tree)] = []
        
        static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
            return lhs.rootData == rhs.rootData &&
                lhs.children.elementsEqual(rhs.children, by: ==)
        }
    }

···

On 9. Jul 2017, at 21:00, Jens Persson via swift-users <swift-users@swift.org> wrote:

On Sun, Jul 9, 2017 at 8:44 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jul 9, 2017, at 10:06, David Baraff via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com <mailto:jens@bitcycle.com>> wrote:

(Also, note that your implementation of == uses lhs === rhs thus will only return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}

Right, the `==` func is *defined* for 2-element tuples where both elements conform to `Equatable`, but that tuple type doesn't itself *conform* to `Equatable`. So the`==` func that's defined on "Array where Element: Equatable" can't see it.

We'd need both "conditional conformance" and "tuple conformance" in order for that to Just Work.

- Dave Sweeris

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(David Baraff) #12

Nice: i hadn’t seen elementsEqual.

  (1) Why do you have to pass in “by: ==“ ? is not that the default

  (2) not a big deal, but if the sequence type’s length can be determined a priori (e.g. in the case of an Array, or perhaps a Collection if that has a count member, haven’t checked) does the elementsEqual function short circuit by first checking that the lengths are equal before beginning the loop?

But again, that’s a great one to know.

···

On Jul 9, 2017, at 12:14 PM, Martin R via swift-users <swift-users@swift.org> wrote:

On 9. Jul 2017, at 21:00, Jens Persson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Since Array has .elementsEqual, another workaround (until conditional conformance) is:

class Tree : Equatable {
    let rootData:Int
    let children:[(String, Tree)]
    
    init(rootData: Int, children: [(String, Tree)]) {
        self.rootData = rootData
        self.children = children
    }
    static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
        return lhs.rootData == rhs.rootData &&
            lhs.children.elementsEqual(rhs.children, by: { (a: (String, Tree), b: (String, Tree)) -> Bool in
                return a.0 == b.0 && a.1 == b.1
            })
    }
}

Slightly simpler (since == is already defined for the tuples):

    class Tree : Equatable {
        let rootData:Int = 0
        let children:[(String, Tree)] = []
        
        static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
            return lhs.rootData == rhs.rootData &&
                lhs.children.elementsEqual(rhs.children, by: ==)
        }
    }

On Sun, Jul 9, 2017 at 8:44 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jul 9, 2017, at 10:06, David Baraff via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com <mailto:jens@bitcycle.com>> wrote:

(Also, note that your implementation of == uses lhs === rhs thus will only return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}

Right, the `==` func is *defined* for 2-element tuples where both elements conform to `Equatable`, but that tuple type doesn't itself *conform* to `Equatable`. So the`==` func that's defined on "Array where Element: Equatable" can't see it.

We'd need both "conditional conformance" and "tuple conformance" in order for that to Just Work.

- Dave Sweeris

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Martin R) #13

Nice: i hadn’t seen elementsEqual.

  (1) Why do you have to pass in “by: ==“ ? is not that the default

There are two versions: One taking an explicit predicate, and another which requires sequences of Equatable elements. As observed earlier in this thread, an array of tuples is not Equatable.

  (2) not a big deal, but if the sequence type’s length can be determined a priori (e.g. in the case of an Array, or perhaps a Collection if that has a count member, haven’t checked) does the elementsEqual function short circuit by first checking that the lengths are equal before beginning the loop?

As far as I can tell from the implementation at https://github.com/apple/swift/blob/master/stdlib/public/core/SequenceAlgorithms.swift.gyb#L292 , the length is not checked a-priori, so you might want to add that to your code:

    class Tree : Equatable {
        let rootData:Int = 0
        let children:[(String, Tree)] = []
        
        static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
            return lhs.rootData == rhs.rootData &&
                lhs.children.count == rhs.children.count &&
                lhs.children.elementsEqual(rhs.children, by: ==)
        }
    }

···

On 9. Jul 2017, at 21:20, David Baraff <davidbaraff@gmail.com> wrote:

But again, that’s a great one to know.

On Jul 9, 2017, at 12:14 PM, Martin R via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On 9. Jul 2017, at 21:00, Jens Persson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Since Array has .elementsEqual, another workaround (until conditional conformance) is:

class Tree : Equatable {
    let rootData:Int
    let children:[(String, Tree)]
    
    init(rootData: Int, children: [(String, Tree)]) {
        self.rootData = rootData
        self.children = children
    }
    static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
        return lhs.rootData == rhs.rootData &&
            lhs.children.elementsEqual(rhs.children, by: { (a: (String, Tree), b: (String, Tree)) -> Bool in
                return a.0 == b.0 && a.1 == b.1
            })
    }
}

Slightly simpler (since == is already defined for the tuples):

    class Tree : Equatable {
        let rootData:Int = 0
        let children:[(String, Tree)] = []
        
        static public func ==(_ lhs:Tree, _ rhs:Tree) -> Bool {
            return lhs.rootData == rhs.rootData &&
                lhs.children.elementsEqual(rhs.children, by: ==)
        }
    }

On Sun, Jul 9, 2017 at 8:44 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jul 9, 2017, at 10:06, David Baraff via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Jul 9, 2017, at 8:27 AM, Jens Persson <jens@bitcycle.com <mailto:jens@bitcycle.com>> wrote:

(Also, note that your implementation of == uses lhs === rhs thus will only return true when lhs and rhs are the same instance of SomeClass.)

Of course — i threw that in just to make a simple example.

Followup question: what I really wanted to write was an == operator for a tree:

// silly tree, useful for nothing
class Tree : Equatable {
   let rootData:Int
   let children:[(String, Tree)]

   static public func ==(_ lhs:Tree, _ rhs:Tree) {
  return lhs.rootData == rhs.rootData &&
            lhs.children == rhs.children // sadly, this doesn’t compile
   }
}

Right, the `==` func is *defined* for 2-element tuples where both elements conform to `Equatable`, but that tuple type doesn't itself *conform* to `Equatable`. So the`==` func that's defined on "Array where Element: Equatable" can't see it.

We'd need both "conditional conformance" and "tuple conformance" in order for that to Just Work.

- Dave Sweeris

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users