Question

I have a record where one field is of type byte[]. Since Clojure equality is not defined for arrays, we have:

(defrecord Record [^bytes field])
(def rec1 (->Record (byte-array (map byte "abc"))))
(def rec2 (->Record (byte-array (map byte "abc"))))

(= rec1 rec2)
; => false

This is, of course, not acceptable.

Now, we do have java.util.Arrays.equals which defines proper equality for arrays. Since = works so well for other Clojure objects, I don't want to use interop every time I compare my Record type, types including that type etc.

Is it possible to override the equals function for a record to handle arrays as well?


I have tried the following:

(defrecord Record [^bytes field])
  Object
    (equals [this other] (Arrays/equals this other)))

But I get an error, "Duplicate method name&signature".

Was it helpful?

Solution

The reason arrays don't compare as equal, in clojure, is because they can change at any time, and objects which are equal should stay equal. Imagine a map like {rec1 1}. If your two objects compare as equal, I should be able to look up rec2 in it and find the answer 1. But if someone does (aset (:field rec2) 1 3), suddenly rec2 has changed, and is no longer equal to rec1!. This is just not something that should happen, so clojure says they're not equal.

This is similar to the equality semantics for atoms and other mutable containers: just because two mutable containers (atoms, or arrays) have equal contents now doesn't mean the containers are themselves equal. That tenuous equality could change at any time, when someone modifies one of them. Similarly, your two arrays may become unequal at any time; this means that, really, they were never equal at all.

OTHER TIPS

Arrays are mutable and thus not values. Clojure equality depends on the immutability of component values in composite things like records. Arrays are dirty mutable things - as a general starting point, just don't do this.

If you really really want to use arrays and you need custom semantics, use a deftype and define your own equals method. But you need to be aware you are then "off the map" as far as Clojure is considered - this is not "normal" Clojure.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top