Clojure

Records of different types with the same data have the same hashcodes, even though they are not considered to be equal

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Completed
  • Affects Version/s: Release 1.3
  • Fix Version/s: Release 1.5
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code
  • Approval:
    Ok

Description

Records which contain the same field values are not considered to be equal, but the record type is not currently included in the hash. This means that if an heterogenous map contains records of different types, where the fields aren't necessarily distinct, there is a high likelyhood of hash collisions.

I propose that the record type should be included in the hash code algorithm for records. There should be no expectation that unequal records of different types should have the same hash-code.

user=> (hash (->A 1))
1013911913
user=> (hash (->B 1))
1013911913
user=> (= (>A 1) (>B 1))
false

This issue also affects ClojureScript. But see CLJS-97, as ClojureScript also has an issue with record equality.

Activity

Hide
David Powell added a comment -

Potential patch to xor the hash of the record symbol name with the current hash obtained from the data

Show
David Powell added a comment - Potential patch to xor the hash of the record symbol name with the current hash obtained from the data
Hide
Rich Hickey added a comment -

You can't change hashCode w/o breaking Map semantics. But you can implement IHashEq and do this in hasheq()

Show
Rich Hickey added a comment - You can't change hashCode w/o breaking Map semantics. But you can implement IHashEq and do this in hasheq()
Hide
Brenton Ashworth added a comment -

Added patch

clj-867-incorporate-record-name-into-hash-code.txt

which implements IHashEq for records, incorporating the record name into the hash code in the hasheq method.

With this patch you will now see the following behavior:

user=> (defrecord A [x])
user.A
user=> (defrecord B [x])
user.B
user=> (= (->B 1) (->A 1))
false
user=> (= (hash (->B 1)) (hash (->A 1)))
false
Show
Brenton Ashworth added a comment - Added patch clj-867-incorporate-record-name-into-hash-code.txt which implements IHashEq for records, incorporating the record name into the hash code in the hasheq method. With this patch you will now see the following behavior:
user=> (defrecord A [x])
user.A
user=> (defrecord B [x])
user.B
user=> (= (->B 1) (->A 1))
false
user=> (= (hash (->B 1)) (hash (->A 1)))
false

People

Vote (0)
Watch (3)

Dates

  • Created:
    Updated:
    Resolved: