Can we `override var hashValue`?


(Zhao Xin) #1

In Objective-C, it says

If two objects are equal, they must have the same hash value. This last

point is particularly important if you define isEqual: in a subclass and
intend to put instances of that subclass into a collection. Make sure you
also define hash in your subclass.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/index.html#//apple_ref/occ/intfm/NSObject/isEqual:

​So the conclusion is that hash can be override.

However, in Swift, things go a little odd. Below code works in both Swift
2.2(Xcode 7.3.1) and Swift 3.0(Xcode 8 beta 6).

func ==(lhs: Fruit, rhs: Fruit) -> Bool {

    print(lhs.hashValue)

    print(rhs.hashValue)

    return lhs.name == rhs.name

}

func ==(lhs: Apple, rhs: Apple) -> Bool {

    return lhs.name == rhs.name && lhs.shape == rhs.shape

}

func ==(lhs: Banana, rhs: Banana) -> Bool {

    return lhs.name == rhs.name && lhs.shape == rhs.shape

}

class Fruit:Hashable {

    let name:String

    var hashValue: Int {

        return 0

    }

    init(_ name:String = "common fruit") {

        self.name = name

    }

}

enum FruitShape:Int {

    case small = 1000

    case medium = 2000

    case big = 3000

}

class Apple:Fruit {

    let shape:FruitShape = .medium

    override var hashValue: Int {

        return 5

    }

}

class Banana:Fruit {

    let shape:FruitShape = .big

    override var hashValue: Int {

        return 10

    }

}

let apple = Apple()

let banana = Banana()

print(apple == banana)

/*

5

10

true
*/

My question is, apple equals banana, but their hashValues (in their own
types) don't. What's wrong here? Is that means we shouldn't override
hashValue in subclass in Swift?

Zhaoxin


(Dmitri Gribenko) #2

func ==(lhs: Apple, rhs: Apple) -> Bool {
    return lhs.name == rhs.name && lhs.shape == rhs.shape
}

func ==(lhs: Banana, rhs: Banana) -> Bool {
    return lhs.name == rhs.name && lhs.shape == rhs.shape
}

There is no reason to compare the shape, it is a constant in each of
these types. (So I am not sure what your point is.)

My question is, apple equals banana, but their hashValues (in their own
types) don't. What's wrong here? Is that means we shouldn't override
hashValue in subclass in Swift?

This means you should not override hashValue in this particular way.
If two values are equal, their hash values should be equal. As long
as your override implementation guarantees this, you can override
hashValue.

You should also understand that the ==(Apple, Apple) and ==(Banana,
Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

Dmitri

···

On Sat, Sep 3, 2016 at 1:31 AM, Zhao Xin via swift-users <swift-users@swift.org> wrote:

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Zhao Xin) #3

There is no reason to compare the shape, it is a constant in each of

these types. (So I am not sure what your point is.)

Sorry. The `let shape` should be `var shape`. I just wanted to make the
subclass to be something more than the super class.

If two values are equal, their hash values should be equal. As long

as your override implementation guarantees this, you can override
hashValue.

But the question is how? If this must be guaranteed by the subclass, how to
writing the override? Or it just can't be done?

You should also understand that the ==(Apple, Apple) and ==(Banana,

Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

In fact, in my example code, `apple` and `banana` is instance of `Apple`
and `Banana`. They are not using `let apple:Fruit = Apple()`. The `==` used
the *`Fruit` version* as it was the only appropriate one. My big question
is, since they used the `*Fruit` version*, and the *`Fruit` version of
`hashValue`* could guarantee the `hashValue` equality, isn't that enough?

Zhaoxin

···

On Sat, Sep 3, 2016 at 7:02 AM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Sat, Sep 3, 2016 at 1:31 AM, Zhao Xin via swift-users > <swift-users@swift.org> wrote:
> func ==(lhs: Apple, rhs: Apple) -> Bool {
> return lhs.name == rhs.name && lhs.shape == rhs.shape
> }
>
> func ==(lhs: Banana, rhs: Banana) -> Bool {
> return lhs.name == rhs.name && lhs.shape == rhs.shape
> }

There is no reason to compare the shape, it is a constant in each of
these types. (So I am not sure what your point is.)

> My question is, apple equals banana, but their hashValues (in their own
> types) don't. What's wrong here? Is that means we shouldn't override
> hashValue in subclass in Swift?

This means you should not override hashValue in this particular way.
If two values are equal, their hash values should be equal. As long
as your override implementation guarantees this, you can override
hashValue.

You should also understand that the ==(Apple, Apple) and ==(Banana,
Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Zhao Xin) #4

The key is how to write the `==` function. It should compare the`
dynamicType`(or `type(of:)` in Swift 3.0) if the class is not a final class.

func ==(lhs: Fruit, rhs: Fruit) -> Bool {

    print(lhs.hashValue)

    print(rhs.hashValue)

    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name

}

func ==(lhs: Apple, rhs: Apple) -> Bool {

    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name && lhs.shape
== rhs.shape

}

func ==(lhs: Banana, rhs: Banana) -> Bool {

    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name && lhs.shape
== rhs.shape

}

class Fruit:Hashable {

    let name:String

    var hashValue: Int {

        return 0

    }

    init(_ name:String = "common fruit") {

        self.name = name

    }

}

enum FruitShape:Int {

    case small = 1000

    case medium = 2000

    case big = 3000

}

class Apple:Fruit {

    let shape:FruitShape

    override var hashValue: Int {

        return 5

    }

    required init(_ name:String = "common fruit", shape:FruitShape = .medium)
{

        self.shape = shape

        super.init(name)

    }

}

class Banana:Fruit {

    let shape:FruitShape

    override var hashValue: Int {

        return 10

    }

    required init(_ name:String = "common fruit", shape:FruitShape = .medium)
{

        self.shape = shape

        super.init(name)

    }

}

let apple = Apple()

let banana = Banana()

print(apple == banana)

/*

5

10

false

*/

I got the idea from book "Core Java", mine is version 8, the latest is the
version 10. I learnt how to writing Object oriented code from it. I am glad
it is still useful.

Zhaoxin

···

On Sat, Sep 3, 2016 at 9:14 AM, Zhao Xin <owenzx@gmail.com> wrote:

There is no reason to compare the shape, it is a constant in each of

these types. (So I am not sure what your point is.)

Sorry. The `let shape` should be `var shape`. I just wanted to make the
subclass to be something more than the super class.

If two values are equal, their hash values should be equal. As long

as your override implementation guarantees this, you can override
hashValue.

But the question is how? If this must be guaranteed by the subclass, how
to writing the override? Or it just can't be done?

You should also understand that the ==(Apple, Apple) and ==(Banana,

Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

In fact, in my example code, `apple` and `banana` is instance of `Apple`
and `Banana`. They are not using `let apple:Fruit = Apple()`. The `==` used
the *`Fruit` version* as it was the only appropriate one. My big question
is, since they used the `*Fruit` version*, and the *`Fruit` version of
`hashValue`* could guarantee the `hashValue` equality, isn't that enough?

Zhaoxin

On Sat, Sep 3, 2016 at 7:02 AM, Dmitri Gribenko <gribozavr@gmail.com> > wrote:

On Sat, Sep 3, 2016 at 1:31 AM, Zhao Xin via swift-users >> <swift-users@swift.org> wrote:
> func ==(lhs: Apple, rhs: Apple) -> Bool {
> return lhs.name == rhs.name && lhs.shape == rhs.shape
> }
>
> func ==(lhs: Banana, rhs: Banana) -> Bool {
> return lhs.name == rhs.name && lhs.shape == rhs.shape
> }

There is no reason to compare the shape, it is a constant in each of
these types. (So I am not sure what your point is.)

> My question is, apple equals banana, but their hashValues (in their own
> types) don't. What's wrong here? Is that means we shouldn't override
> hashValue in subclass in Swift?

This means you should not override hashValue in this particular way.
If two values are equal, their hash values should be equal. As long
as your override implementation guarantees this, you can override
hashValue.

You should also understand that the ==(Apple, Apple) and ==(Banana,
Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Jordan Rose) #5

As I’ve said before, this doesn’t work.

let x: Fruit = Apple(“a”, shape: .medium)
let y: Fruit = Apple(“a”, shape: .big)
print(x == y) // will print true

If you want == to behave differently for subclasses, it needs to call a method that can be overridden. Static overloads will not produce the same behavior.

Jordan

···

On Sep 2, 2016, at 18:42, Zhao Xin via swift-users <swift-users@swift.org> wrote:

The key is how to write the `==` function. It should compare the` dynamicType`(or `type(of:)` in Swift 3.0) if the class is not a final class.

func ==(lhs: Fruit, rhs: Fruit) -> Bool {
    
    print(lhs.hashValue)
    print(rhs.hashValue)
    
    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name
}

func ==(lhs: Apple, rhs: Apple) -> Bool {
    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name && lhs.shape == rhs.shape
}

func ==(lhs: Banana, rhs: Banana) -> Bool {
    return type(of:lhs) == type(of:rhs) && lhs.name == rhs.name && lhs.shape == rhs.shape
}

class Fruit:Hashable {
    let name:String
    
    var hashValue: Int {
        return 0
    }
    
    init(_ name:String = "common fruit") {
        self.name = name
    }
}

enum FruitShape:Int {
    case small = 1000
    case medium = 2000
    case big = 3000
}

class Apple:Fruit {
    let shape:FruitShape
    
    override var hashValue: Int {
        return 5
    }
    
    required init(_ name:String = "common fruit", shape:FruitShape = .medium) {
        self.shape = shape
        super.init(name)
    }
}

class Banana:Fruit {
    let shape:FruitShape
    
    override var hashValue: Int {
        return 10
    }
    
    required init(_ name:String = "common fruit", shape:FruitShape = .medium) {
        self.shape = shape
        super.init(name)
    }
}

let apple = Apple()
let banana = Banana()

print(apple == banana)
/*
5
10
false
*/

I got the idea from book "Core Java", mine is version 8, the latest is the version 10. I learnt how to writing Object oriented code from it. I am glad it is still useful.

Zhaoxin

On Sat, Sep 3, 2016 at 9:14 AM, Zhao Xin <owenzx@gmail.com <mailto:owenzx@gmail.com>> wrote:
There is no reason to compare the shape, it is a constant in each of
these types. (So I am not sure what your point is.)

Sorry. The `let shape` should be `var shape`. I just wanted to make the subclass to be something more than the super class.

If two values are equal, their hash values should be equal. As long
as your override implementation guarantees this, you can override
hashValue.

But the question is how? If this must be guaranteed by the subclass, how to writing the override? Or it just can't be done?

You should also understand that the ==(Apple, Apple) and ==(Banana,
Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

In fact, in my example code, `apple` and `banana` is instance of `Apple` and `Banana`. They are not using `let apple:Fruit = Apple()`. The `==` used the `Fruit` version as it was the only appropriate one. My big question is, since they used the `Fruit` version, and the `Fruit` version of `hashValue` could guarantee the `hashValue` equality, isn't that enough?

Zhaoxin

On Sat, Sep 3, 2016 at 7:02 AM, Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>> wrote:
On Sat, Sep 3, 2016 at 1:31 AM, Zhao Xin via swift-users > <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
> func ==(lhs: Apple, rhs: Apple) -> Bool {
> return lhs.name <http://lhs.name/> == rhs.name <http://rhs.name/> && lhs.shape == rhs.shape
> }
>
> func ==(lhs: Banana, rhs: Banana) -> Bool {
> return lhs.name <http://lhs.name/> == rhs.name <http://rhs.name/> && lhs.shape == rhs.shape
> }

There is no reason to compare the shape, it is a constant in each of
these types. (So I am not sure what your point is.)

> My question is, apple equals banana, but their hashValues (in their own
> types) don't. What's wrong here? Is that means we shouldn't override
> hashValue in subclass in Swift?

This means you should not override hashValue in this particular way.
If two values are equal, their hash values should be equal. As long
as your override implementation guarantees this, you can override
hashValue.

You should also understand that the ==(Apple, Apple) and ==(Banana,
Banana) are not overrides for ==(Fruit, Fruit), and they would not be
called through dynamic dispatch when you have, for example, two apples
typed as fruits.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>>*/

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


(Lou Zell) #6

My question is, apple equals banana, but their hashValues (in their own
types) don't. What's wrong here?


Hi Zhao. In addition to what Jordan and Dmitri have said, I think part of
the confusion is that you are assuming hash values are implicitly used in
an equality check. They are not. They are used when your instances are
added to certain types of collections.

In your first example, where you print out the hash values but then compare
lhs.name to rhs.name, the names of the two fruits are both "common fruit",
and the equality test returns true. Hash never comes into play. You can
test for yourself when the hash gets used:

import Foundation
class Foo: NSObject {
    override var hash: Int {
        print("Computing hash value!")
        return 1
    }
}
var f1 = Foo()
var f2 = Foo()
f1 == f2 // Doesn't print anything!
var aSet = Set<Foo>()
aSet.insert(f1) // Prints "Computing has value!"

Lou


(Zhao Xin) #7

Thanks Lou.

In addition to what Jordan and Dmitri have said, I think part of the

confusion is that you are assuming hash values are implicitly used in an
equality check. They are not. They are used when your instances are added
to certain types of collections.

​You are very nice. But I was not assuming hash values were implicitly used
in an equality check. In fact, my problem was solved on the third email of
mine in this thread.

Here is the history of the question and the answer.

1. On the point of "Can we `override var hashValue`", I was and am on the
side of definitely "Yes". And Jordan Rose, was on the side of perhaps, "but
someone should carefully implemented the `hashValue` on base class and
subclasses, so that the rule "if `a==b`, then `a.hashValue==b.hashValue`
wouldn't be violated".

2. At the beginning, I still thought we could `override var hashValue`. But
it violated the rule with comparing between instances from different
subclasses. My explanation was that since the hashValue on the base class
was not violated the rule, the rule was not broken. Also I insisted that
thought it was not documented, the base of equality should be the types of
instances equaled at first. I knew my point was weak, so I asked in this
thread to see if there was something I missed.

3. I got the reply from Michael Nisi. But I thought his point was alike
Jordan's. I knew I was gotten the point of " the base of equality should be
the types of instances equaled at first" from somewhere. So I looked up.
Turned out I found it in the book "Core Java".

4. The conclusion: The rule should not be violated at any time. My previous
explanation of "since the hashValue on the base class was not violated the
rule, the rule was not broken" was wrong. However, Jordan's point "someone
should carefully implemented the `hashValue` on base class and subclass, so
that the rule "if `a==b`, then `a.hashValue==b.hashValue` wouldn't be
violated. " was also not correct. As there was no way to `override
hashValue` like that.

Here are my points on " Can we `override var hashValue`":

1. We can. But we should firstly test the `type(of:lhs) == type(of:rhs)`.
If that doesn't equal, we return false. This technique solved the problem
of mine in this thread. This should be the first choice of all situations.

2. We can but we may prefer not to. If we choose not to, we must keep that
to all subclasses of the base class. I don't prefer this way, but someone
may like it. I think doing this may gain more problems than it solves. But
again, since it does not violate any documented rules, it could exist.

3. In both 1 and 2, we must also write `==` for each subclasses.

Zhaoxin

···

On Wed, Sep 7, 2016 at 5:55 AM, Lou Zell <lzell11@gmail.com> wrote:

My question is, apple equals banana, but their hashValues (in their own

types) don't. What's wrong here?


Hi Zhao. In addition to what Jordan and Dmitri have said, I think part of
the confusion is that you are assuming hash values are implicitly used in
an equality check. They are not. They are used when your instances are
added to certain types of collections.

In your first example, where you print out the hash values but then
compare lhs.name to rhs.name, the names of the two fruits are both
"common fruit", and the equality test returns true. Hash never comes into
play. You can test for yourself when the hash gets used:

import Foundation
class Foo: NSObject {
    override var hash: Int {
        print("Computing hash value!")
        return 1
    }
}
var f1 = Foo()
var f2 = Foo()
f1 == f2 // Doesn't print anything!
var aSet = Set<Foo>()
aSet.insert(f1) // Prints "Computing has value!"

Lou