Equals and HashCode Builder
Hello! Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apac... and https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apac... ? Thanks in advance, Vitor <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>. <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
Not that I know. Because it can be a little project. Now what is the key point to encapsulate this computation? On Mon, Oct 2, 2017 at 12:51 AM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/ api-release/org/apache/commons/lang3/builder/EqualsBuilder.html
and
https://commons.apache.org/proper/commons-lang/javadocs/ api-release/org/apache/commons/lang3/builder/HashCodeBuilder.html?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>. <#m_7375944706768832526_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
in java, because this is a pain to do it each time. for us, maybe for the same⦠even if is a lot easier. Esteban
On 2 Oct 2017, at 09:42, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Not that I know. Because it can be a little project. Now what is the key point to encapsulate this computation?
On Mon, Oct 2, 2017 at 12:51 AM, Vitor Medina Cruz <vitormcruz@gmail.com <mailto:vitormcruz@gmail.com>> wrote: Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apac... <https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apac...>
and
https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apac... <https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/builder/HashCodeBuilder.html>?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>. <x-msg://32/#m_7375944706768832526_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
Hi, I wrote a generator for that. Probably it makes no sense to publish it as a standalone project. I tend to think code generation fits better in other languages. I can paste the relevant code here if needed. 2017-10-02 9:44 GMT+02:00 Esteban Lorenzano <estebanlm@gmail.com>:
in java, because this is a pain to do it each time. for us, maybe for the same⦠even if is a lot easier.
Esteban
On 2 Oct 2017, at 09:42, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Not that I know. Because it can be a little project. Now what is the key point to encapsulate this computation?
On Mon, Oct 2, 2017 at 12:51 AM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/EqualsBuilder.html
and
https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/HashCodeBuilder.html?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>.
Publish it in a package and let us have a look. On Mon, Oct 2, 2017 at 9:53 AM, Jose San Leandro <jose.sanleandro@osoco.es> wrote:
Hi,
I wrote a generator for that. Probably it makes no sense to publish it as a standalone project. I tend to think code generation fits better in other languages. I can paste the relevant code here if needed.
2017-10-02 9:44 GMT+02:00 Esteban Lorenzano <estebanlm@gmail.com>:
in java, because this is a pain to do it each time. for us, maybe for the same⦠even if is a lot easier.
Esteban
On 2 Oct 2017, at 09:42, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Not that I know. Because it can be a little project. Now what is the key point to encapsulate this computation?
On Mon, Oct 2, 2017 at 12:51 AM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/EqualsBuilder.html
and
https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/HashCodeBuilder.html?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>.
*in java, because this is a pain to do it each time.* *for us, maybe for the same⦠even if is a lot easier.* Yeah, and it is boring to implement that every time, especially because it is tricky to get it right sometimes. Another interesting thing I use in Java to test my equals and hash implementation is the http://jqno.nl/equalsverifier/ I just use RBGenerateEqualHashRefactoring for this: Oh! That is nice, I will give a deeper look at it, I think this may do the job. Thanks! On Mon, Oct 2, 2017 at 10:09 AM, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Publish it in a package and let us have a look.
On Mon, Oct 2, 2017 at 9:53 AM, Jose San Leandro <jose.sanleandro@osoco.es
wrote:
Hi,
I wrote a generator for that. Probably it makes no sense to publish it as a standalone project. I tend to think code generation fits better in other languages. I can paste the relevant code here if needed.
2017-10-02 9:44 GMT+02:00 Esteban Lorenzano <estebanlm@gmail.com>:
in java, because this is a pain to do it each time. for us, maybe for the same⦠even if is a lot easier.
Esteban
On 2 Oct 2017, at 09:42, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Not that I know. Because it can be a little project. Now what is the key point to encapsulate this computation?
On Mon, Oct 2, 2017 at 12:51 AM, Vitor Medina Cruz <vitormcruz@gmail.com
wrote:
Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/EqualsBuilder.html
and
https://commons.apache.org/proper/commons-lang/javadocs/api- release/org/apache/commons/lang3/builder/HashCodeBuilder.html?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>.
Hi. I just use RBGenerateEqualHashRefactoring for this: r := RBGenerateEqualHashRefactoring className: MyClass variables: #(vars which should be used in equal and hash). r execute But it is a bit different. 2017-10-02 0:51 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
Hello!
Is there, in Pharo, builders for equals (=) and hash messages similar to: https://commons.apache.org/proper/commons-lang/javadocs/ api-release/org/apache/commons/lang3/builder/EqualsBuilder.html
and
https://commons.apache.org/proper/commons-lang/javadocs/ api-release/org/apache/commons/lang3/builder/HashCodeBuilder.html?
Thanks in advance, Vitor
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...> Livre de vÃrus. www.avg.com <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_camp...>. <#m_4862217785176417547_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
Denis Kudriashov wrote
I just use RBGenerateEqualHashRefactoring for this:
r := RBGenerateEqualHashRefactoring className: MyClass variables: #(vars which should be used in equal and hash). r execute
That is sooooo cool! I shudder to think how many times I implemented just that by hand!! Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that? 2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing something⦠----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Hi Sean. I did not know about quality of generated hash function. But if I remember correctly the same code is generated for java using Eclipse or Idea. So it should be good enough. 2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Denis Kudriashov wrote
I just use RBGenerateEqualHashRefactoring for this:
r := RBGenerateEqualHashRefactoring className: MyClass variables: #(vars which should be used in equal and hash). r execute
That is sooooo cool! I shudder to think how many times I implemented just that by hand!!
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
I am sorry, not species, but #isKindOf istead of #= to compare classes. On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive. How you will generate it with isKindOf: logic? You need to know common parent. Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Yes, canEqual implementation also make #= be commutative. On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Noury also proposed a trait to avoid to systematically have to implement hash. Vitor why don't you take it as a small project to propose a solution for Pharo. On Thu, Oct 5, 2017 at 6:34 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Yes, canEqual implementation also make #= be commutative.
On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Here is what I extracted from older emails
From David Allouche in Pharo-dev to see if The ==hash== method is used in Pharo to separate objects in bucket-based data structures.
!! Property It must have one required property which is required data integrity: !!!! A. Objects that are equal according to ==\=== must have equal hashes. It should have two desirable properties that are required to support the algorithmic assumptions of bucket-based data structures: !!!! B. Objects that are not equal should have different hashes. Ideally, the probability of hash collision should by 1/N where N is the size of hash value space. !!!! C. The hash values should spread as much as possible over their value space. For example: ProtoObject>>#identityHash. How these two desirable properties can be implemented depends on the actual distribution of the values used to compute the hash. An object can safely use XOR hashing if its instance variables are all different objects that already provide a hash method with these two properties. @@todo check following sentence because the use of singleton is obscure For example, an object whose behaviour is defined by several of singleton delegates of different classes that do not override Object>>#hash. But if the instance variables are not guaranteed to have those properties, it is necessary to more thoroughly "mix the bits". For example the method ==hash== of ==SequenceableCollection== illustrates this point. [[[ SequenceableCollection >> hash | hash | hash := self species hash. 1 to: self size do: [:i | hash := (hash + (self at: i) hash) hashMultiply]. ^ hash ]]] The mixing of the bits is done by the combination of addition and ==hashMultiply==. The value of ==species hash== does not need to be processed by ==hashMultiply==, because it is probably computed by the method =identityHash== of ==ProtoObject==, and that already provides property C. !!! When we should not use XOR ==LayoutFrame== is a particularly good example of a data structure that should not use XOR hashing. Its instance variables provide none of the required properties: - ==SmallInteger>>hash== is just ==^self==. - In addition, common ==LayoutFrame== instances tend to use pairs of identical values in their instance variables. Like ==0@0 corner: 1@1==, or ==Margin fromNumber: 10==. A good hash function for LayoutFrame needs to: - Get hashes for each instance variable that provides properties A and B. - Combine them in a way that is order-sensitive (to maintain property B) and that does some extra mixing (to provide property C). Now, we can assume that common values for the instance variables will be instances of ==SmallInteger==, ==Float==, or ==Fraction==. You can see in ==Fraction>>hash==, that a comment mentions the assumption that the fraction is already reduced, that is what makes it acceptable to use bitXor. The hash of ==SmallInteger== does provide properties A and B, but not property C. The hash of ==Float== is harder to understand, but tests show that it provides distinct values for 0.5, 0.25 and 0.125. So it hopefully provides B for the range of values of interest to ==LayoutFrame==. Another class that has similar hashing constraints to ==LayoutFrame== is ==Point==. Its ==hash== method is: [[[ Point >> hash "Hash is reimplemented because = is implemented." ^(x hash hashMultiply + y hash) hashMultiply ]]] It does not include species in the computation, which is less than ideal because it favours collisions with objects of different species that have a similar content and a the same hash algorithm. Finally, it is probably not necessary or useful to use the accessors to get to the instance variables. So a good implementation for LayoutFrame is [[[ LayoutFrame >> hash | hash | hash := self species hash hash := (hash + leftFraction hash) hashMultiply hash := (hash + leftOffset hash) hashMultiply hash := (hash + topFraction hash) hashMultiply hash := (hash + topOffset hash) hashMultiply hash := (hash + rightFraction hash) hashMultiply hash := (hash + rightOffset hash) hashMultiply hash := (hash + bottomFraction hash) hashMultiply hash := (hash + bottomOffset hash) hashMultiply ^ hash ]]] On Fri, Oct 6, 2017 at 9:59 AM, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Noury also proposed a trait to avoid to systematically have to implement hash. Vitor why don't you take it as a small project to propose a solution for Pharo.
On Thu, Oct 5, 2017 at 6:34 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Yes, canEqual implementation also make #= be commutative.
On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Stephane "Vitor why don't you take it as a small project to propose a solution for Pharo." I will try :) I like the idea of using a Trait. Em 6 de out de 2017 05:01, "Stephane Ducasse" <stepharo.self@gmail.com> escreveu: Here is what I extracted from older emails
From David Allouche in Pharo-dev to see if The ==hash== method is used in Pharo to separate objects in bucket-based data structures.
!! Property It must have one required property which is required data integrity: !!!! A. Objects that are equal according to ==\=== must have equal hashes. It should have two desirable properties that are required to support the algorithmic assumptions of bucket-based data structures: !!!! B. Objects that are not equal should have different hashes. Ideally, the probability of hash collision should by 1/N where N is the size of hash value space. !!!! C. The hash values should spread as much as possible over their value space. For example: ProtoObject>>#identityHash. How these two desirable properties can be implemented depends on the actual distribution of the values used to compute the hash. An object can safely use XOR hashing if its instance variables are all different objects that already provide a hash method with these two properties. @@todo check following sentence because the use of singleton is obscure For example, an object whose behaviour is defined by several of singleton delegates of different classes that do not override Object>>#hash. But if the instance variables are not guaranteed to have those properties, it is necessary to more thoroughly "mix the bits". For example the method ==hash== of ==SequenceableCollection== illustrates this point. [[[ SequenceableCollection >> hash | hash | hash := self species hash. 1 to: self size do: [:i | hash := (hash + (self at: i) hash) hashMultiply]. ^ hash ]]] The mixing of the bits is done by the combination of addition and ==hashMultiply==. The value of ==species hash== does not need to be processed by ==hashMultiply==, because it is probably computed by the method =identityHash== of ==ProtoObject==, and that already provides property C. !!! When we should not use XOR ==LayoutFrame== is a particularly good example of a data structure that should not use XOR hashing. Its instance variables provide none of the required properties: - ==SmallInteger>>hash== is just ==^self==. - In addition, common ==LayoutFrame== instances tend to use pairs of identical values in their instance variables. Like ==0@0 corner: 1@1==, or ==Margin fromNumber: 10==. A good hash function for LayoutFrame needs to: - Get hashes for each instance variable that provides properties A and B. - Combine them in a way that is order-sensitive (to maintain property B) and that does some extra mixing (to provide property C). Now, we can assume that common values for the instance variables will be instances of ==SmallInteger==, ==Float==, or ==Fraction==. You can see in ==Fraction>>hash==, that a comment mentions the assumption that the fraction is already reduced, that is what makes it acceptable to use bitXor. The hash of ==SmallInteger== does provide properties A and B, but not property C. The hash of ==Float== is harder to understand, but tests show that it provides distinct values for 0.5, 0.25 and 0.125. So it hopefully provides B for the range of values of interest to ==LayoutFrame==. Another class that has similar hashing constraints to ==LayoutFrame== is ==Point==. Its ==hash== method is: [[[ Point >> hash "Hash is reimplemented because = is implemented." ^(x hash hashMultiply + y hash) hashMultiply ]]] It does not include species in the computation, which is less than ideal because it favours collisions with objects of different species that have a similar content and a the same hash algorithm. Finally, it is probably not necessary or useful to use the accessors to get to the instance variables. So a good implementation for LayoutFrame is [[[ LayoutFrame >> hash | hash | hash := self species hash hash := (hash + leftFraction hash) hashMultiply hash := (hash + leftOffset hash) hashMultiply hash := (hash + topFraction hash) hashMultiply hash := (hash + topOffset hash) hashMultiply hash := (hash + rightFraction hash) hashMultiply hash := (hash + rightOffset hash) hashMultiply hash := (hash + bottomFraction hash) hashMultiply hash := (hash + bottomOffset hash) hashMultiply ^ hash ]]] On Fri, Oct 6, 2017 at 9:59 AM, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Noury also proposed a trait to avoid to systematically have to implement hash. Vitor why don't you take it as a small project to propose a solution for Pharo.
On Thu, Oct 5, 2017 at 6:34 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Yes, canEqual implementation also make #= be commutative.
On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare
classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <
dionisiydk@gmail.com>
wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species
instead?"
ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Contact noury because I do not remember. But having a little builder is also nice. On Fri, Oct 6, 2017 at 4:15 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Stephane
"Vitor why don't you take it as a small project to propose a solution for Pharo."
I will try :) I like the idea of using a Trait.
Em 6 de out de 2017 05:01, "Stephane Ducasse" <stepharo.self@gmail.com> escreveu:
Here is what I extracted from older emails
From David Allouche in Pharo-dev to see if The ==hash== method is used in Pharo to separate objects in bucket-based data structures.
!! Property
It must have one required property which is required data integrity:
!!!! A. Objects that are equal according to ==\=== must have equal hashes.
It should have two desirable properties that are required to support the algorithmic assumptions of bucket-based data structures:
!!!! B. Objects that are not equal should have different hashes. Ideally, the probability of hash collision should by 1/N where N is the size of hash value space.
!!!! C. The hash values should spread as much as possible over their value space. For example: ProtoObject>>#identityHash.
How these two desirable properties can be implemented depends on the actual distribution of the values used to compute the hash.
An object can safely use XOR hashing if its instance variables are all different objects that already provide a hash method with these two properties.
@@todo check following sentence because the use of singleton is obscure
For example, an object whose behaviour is defined by several of singleton delegates of different classes that do not override Object>>#hash.
But if the instance variables are not guaranteed to have those properties, it is necessary to more thoroughly "mix the bits". For example the method ==hash== of ==SequenceableCollection== illustrates this point.
[[[ SequenceableCollection >> hash | hash | hash := self species hash. 1 to: self size do: [:i | hash := (hash + (self at: i) hash) hashMultiply]. ^ hash ]]]
The mixing of the bits is done by the combination of addition and ==hashMultiply==. The value of ==species hash== does not need to be processed by ==hashMultiply==, because it is probably computed by the method =identityHash== of ==ProtoObject==, and that already provides property C.
!!! When we should not use XOR
==LayoutFrame== is a particularly good example of a data structure that should not use XOR hashing. Its instance variables provide none of the required properties:
- ==SmallInteger>>hash== is just ==^self==. - In addition, common ==LayoutFrame== instances tend to use pairs of identical values in their instance variables. Like ==0@0 corner: 1@1==, or ==Margin fromNumber: 10==.
A good hash function for LayoutFrame needs to:
- Get hashes for each instance variable that provides properties A and B. - Combine them in a way that is order-sensitive (to maintain property B) and that does some extra mixing (to provide property C).
Now, we can assume that common values for the instance variables will be instances of ==SmallInteger==, ==Float==, or ==Fraction==.
You can see in ==Fraction>>hash==, that a comment mentions the assumption that the fraction is already reduced, that is what makes it acceptable to use bitXor.
The hash of ==SmallInteger== does provide properties A and B, but not property C. The hash of ==Float== is harder to understand, but tests show that it provides distinct values for 0.5, 0.25 and 0.125. So it hopefully provides B for the range of values of interest to ==LayoutFrame==.
Another class that has similar hashing constraints to ==LayoutFrame== is ==Point==. Its ==hash== method is:
[[[ Point >> hash "Hash is reimplemented because = is implemented." ^(x hash hashMultiply + y hash) hashMultiply ]]]
It does not include species in the computation, which is less than ideal because it favours collisions with objects of different species that have a similar content and a the same hash algorithm.
Finally, it is probably not necessary or useful to use the accessors to get to the instance variables.
So a good implementation for LayoutFrame is
[[[ LayoutFrame >> hash | hash | hash := self species hash hash := (hash + leftFraction hash) hashMultiply hash := (hash + leftOffset hash) hashMultiply hash := (hash + topFraction hash) hashMultiply hash := (hash + topOffset hash) hashMultiply hash := (hash + rightFraction hash) hashMultiply hash := (hash + rightOffset hash) hashMultiply hash := (hash + bottomFraction hash) hashMultiply hash := (hash + bottomOffset hash) hashMultiply ^ hash ]]]
On Fri, Oct 6, 2017 at 9:59 AM, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Noury also proposed a trait to avoid to systematically have to implement hash. Vitor why don't you take it as a small project to propose a solution for Pharo.
On Thu, Oct 5, 2017 at 6:34 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Yes, canEqual implementation also make #= be commutative.
On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Hi, I have developed few years ago a trait-based solution to make it easy to deal with equality. By default, instances of the same class with equal IVs are equal + have the same hash code. You can customize the list of IVs to compare, which is handy in case of IVs that store cache values. All you have to do is to use the TEquality in your class. The code + tests are here http://smalltalkhub.com/#!/~CAR/ReusableBricks/packages/Equals This is part of the "ReusableBricks" repo that gather packages we're using it in different projects in my team. And it works fine. I'd love to have this integrated in Pharo. Note : I no longer receive mailing-lists emails, because I can't handle all the traffic. We can continue the conversation on Discord Noury
On 06 Oct 2017, at 18:31, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Contact noury because I do not remember. But having a little builder is also nice.
On Fri, Oct 6, 2017 at 4:15 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Stephane
"Vitor why don't you take it as a small project to propose a solution for Pharo."
I will try :) I like the idea of using a Trait.
Em 6 de out de 2017 05:01, "Stephane Ducasse" <stepharo.self@gmail.com> escreveu:
Here is what I extracted from older emails
From David Allouche in Pharo-dev to see if The ==hash== method is used in Pharo to separate objects in bucket-based data structures.
!! Property
It must have one required property which is required data integrity:
!!!! A. Objects that are equal according to ==\=== must have equal hashes.
It should have two desirable properties that are required to support the algorithmic assumptions of bucket-based data structures:
!!!! B. Objects that are not equal should have different hashes. Ideally, the probability of hash collision should by 1/N where N is the size of hash value space.
!!!! C. The hash values should spread as much as possible over their value space. For example: ProtoObject>>#identityHash.
How these two desirable properties can be implemented depends on the actual distribution of the values used to compute the hash.
An object can safely use XOR hashing if its instance variables are all different objects that already provide a hash method with these two properties.
@@todo check following sentence because the use of singleton is obscure
For example, an object whose behaviour is defined by several of singleton delegates of different classes that do not override Object>>#hash.
But if the instance variables are not guaranteed to have those properties, it is necessary to more thoroughly "mix the bits". For example the method ==hash== of ==SequenceableCollection== illustrates this point.
[[[ SequenceableCollection >> hash | hash | hash := self species hash. 1 to: self size do: [:i | hash := (hash + (self at: i) hash) hashMultiply]. ^ hash ]]]
The mixing of the bits is done by the combination of addition and ==hashMultiply==. The value of ==species hash== does not need to be processed by ==hashMultiply==, because it is probably computed by the method =identityHash== of ==ProtoObject==, and that already provides property C.
!!! When we should not use XOR
==LayoutFrame== is a particularly good example of a data structure that should not use XOR hashing. Its instance variables provide none of the required properties:
- ==SmallInteger>>hash== is just ==^self==. - In addition, common ==LayoutFrame== instances tend to use pairs of identical values in their instance variables. Like ==0@0 corner: 1@1==, or ==Margin fromNumber: 10==.
A good hash function for LayoutFrame needs to:
- Get hashes for each instance variable that provides properties A and B. - Combine them in a way that is order-sensitive (to maintain property B) and that does some extra mixing (to provide property C).
Now, we can assume that common values for the instance variables will be instances of ==SmallInteger==, ==Float==, or ==Fraction==.
You can see in ==Fraction>>hash==, that a comment mentions the assumption that the fraction is already reduced, that is what makes it acceptable to use bitXor.
The hash of ==SmallInteger== does provide properties A and B, but not property C. The hash of ==Float== is harder to understand, but tests show that it provides distinct values for 0.5, 0.25 and 0.125. So it hopefully provides B for the range of values of interest to ==LayoutFrame==.
Another class that has similar hashing constraints to ==LayoutFrame== is ==Point==. Its ==hash== method is:
[[[ Point >> hash "Hash is reimplemented because = is implemented." ^(x hash hashMultiply + y hash) hashMultiply ]]]
It does not include species in the computation, which is less than ideal because it favours collisions with objects of different species that have a similar content and a the same hash algorithm.
Finally, it is probably not necessary or useful to use the accessors to get to the instance variables.
So a good implementation for LayoutFrame is
[[[ LayoutFrame >> hash | hash | hash := self species hash hash := (hash + leftFraction hash) hashMultiply hash := (hash + leftOffset hash) hashMultiply hash := (hash + topFraction hash) hashMultiply hash := (hash + topOffset hash) hashMultiply hash := (hash + rightFraction hash) hashMultiply hash := (hash + rightOffset hash) hashMultiply hash := (hash + bottomFraction hash) hashMultiply hash := (hash + bottomOffset hash) hashMultiply ^ hash ]]]
On Fri, Oct 6, 2017 at 9:59 AM, Stephane Ducasse <stepharo.self@gmail.com> wrote:
Noury also proposed a trait to avoid to systematically have to implement hash. Vitor why don't you take it as a small project to propose a solution for Pharo.
On Thu, Oct 5, 2017 at 6:34 PM, Vitor Medina Cruz <vitormcruz@gmail.com> wrote:
Yes, canEqual implementation also make #= be commutative.
On Tue, Oct 3, 2017 at 11:11 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 17:30 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2017-10-02 17:13 GMT+02:00 Vitor Medina Cruz <vitormcruz@gmail.com>:
I am sorry, not species, but #isKindOf istead of #= to compare classes.
It is bad idea. #= should be transitive.
Oh, I used wrong word, shame on me :). I tried to say commutative.
How you will generate it with isKindOf: logic? You need to know common parent.
Also I not remember cases where I was needed two instances of different classes to be equal. And I can imaging the problems which it will lead to.
On Mon, Oct 2, 2017 at 11:57 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:37 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Two questions/comments about the generated code: 1. #= ... self class = anObject class "should compare #species instead?" ifFalse: [ ^ false ]. ... Typically, I've seen #species instead of #class in the guard statement. Should we change it to that?
I doubt that it is important for domain classes. Because I never saw the user of #species which is not a kind of Collection. And for collections this refactoring is not valid anyway.
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Noury Bouraqadi-2 wrote
The code + tests are here http://smalltalkhub.com/#!/~CAR/ReusableBricks/packages/Equals
I forked to https://github.com/seandenigris/Pharo-Equals with a baseline to make it easier to try out/possibly-integrate. ----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
On 2 Oct 2017, at 16:37 , Sean P. DeNigris <sean@clipperadams.com> wrote:
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
If you read Andres book (which is hard, because he wonât make it available online), you will learn that itâs better to take the order of the instance variables into account. #bitXor: is commutative, so it of course ignores order. In other words, if you have a Point with x and y fields, then 3@9 and 9@3 will have the same hash. For this reason, itâs better to multiply successive fields by a constant (e.g. shifting left by 2) before XORing them into the hash. But then, for objects with lots of fields, the later ones are lot completely, because they are multiplied by a number so large that they are out of the range of the hash. One can avoid this by using a circular shift to implement the multiply. All of this was, I thought, implemented in a #hashCombine: method, but I canât find it in my image. Maybe some other Smalltalk ...
Denis, That can be done with an canEqual implementation as described by http://www.artima.com/lejava/articles/equality.html. For this reason, itâs better to multiply successive fields by a constant
(e.g. shifting left by 2) before XORing them into the hash. But then, for objects with lots of fields, the later ones are lot completely, because they are multiplied by a number so large that they are out of the range of the hash.
I think this exactly what HasCodeBuidler does, however I saw no special treatment for hashes made of many fields.... On Mon, Oct 2, 2017 at 12:38 PM, Prof. Andrew P. Black <black@cs.pdx.edu> wrote:
On 2 Oct 2017, at 16:37 , Sean P. DeNigris <sean@clipperadams.com> wrote:
2. #hash ^ var1 hash bitXor: (var2 hash bitXor: var3 hash) Is this implementation always safe? It's what I usually hand roll based on what I've seen, but Andres Valloud wrote a whole (large) book on hashing, so I've always wondered if I was missing somethingâ¦
If you read Andres book (which is hard, because he wonât make it available online), you will learn that itâs better to take the order of the instance variables into account. #bitXor: is commutative, so it of course ignores order.
In other words, if you have a Point with x and y fields, then 3@9 and 9@3 will have the same hash.
For this reason, itâs better to multiply successive fields by a constant (e.g. shifting left by 2) before XORing them into the hash. But then, for objects with lots of fields, the later ones are lot completely, because they are multiplied by a number so large that they are out of the range of the hash.
One can avoid this by using a circular shift to implement the multiply.
All of this was, I thought, implemented in a #hashCombine: method, but I canât find it in my image. Maybe some other Smalltalk ...
Denis Kudriashov wrote
I just use RBGenerateEqualHashRefactoring for this:
I added your answer to the Pharo wiki. Is this available through the UI? If not, maybe it should be! ----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
2017-10-02 16:41 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Denis Kudriashov wrote
I just use RBGenerateEqualHashRefactoring for this:
I added your answer to the Pharo wiki. Is this available through the UI? If not, maybe it should be!
As I know it not exists
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
I think it should have an option to generate with species (which I think should be the default). Also It would be nice if it could implement the solution for pitfall #4 described in http://www.artima.com/lejava/articles/equality.html On Mon, Oct 2, 2017 at 11:45 AM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2017-10-02 16:41 GMT+02:00 Sean P. DeNigris <sean@clipperadams.com>:
Denis Kudriashov wrote
I just use RBGenerateEqualHashRefactoring for this:
I added your answer to the Pharo wiki. Is this available through the UI? If not, maybe it should be!
As I know it not exists
----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
participants (8)
-
Denis Kudriashov -
Esteban Lorenzano -
Jose San Leandro -
Noury Gmail -
Prof. Andrew P. Black -
Sean P. DeNigris -
Stephane Ducasse -
Vitor Medina Cruz