Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maps and records equality in Clojure

I found the behavior of Clojure confusing regarding equality between maps and records. In this first example, we have two different types which are structurally equal. The equality = function returns true:

user> (defn make-one-map
         []
       {:a "a" :b "b"})
#'user/make-one-map
user> (def m1 (make-one-map))
#'user/m1
user> m1
{:a "a", :b "b"}
user> (def m2 {:a "a" :b "b"})
#'user/m2
user> m2
{:a "a", :b "b"}
user> (= m1 m2)
true
user> (type m1)
clojure.lang.PersistentArrayMap
user> (type m2)
clojure.lang.PersistentHashMap

In the second example we have a hashmap and a record which are structurally equivalent but the = function returns false:

user> (defrecord Titi [a b])
user.Titi
user> (def titi (Titi. 1 2))
#'user/titi
user> titi
#user.Titi{:a 1, :b 2}
user> (= titi {:a 1 :b 2})
false

Why are the differences? I'm using Clojure 1.3 and I found them really confusing.

like image 543
z1naOK9nu8iY5A Avatar asked Feb 10 '12 14:02

z1naOK9nu8iY5A


People also ask

How do you check for equality in Clojure?

Equality in Clojure is most often tested using = . Unlike Java's equals method, Clojure's = returns true for many values that do not have the same type as each other. = does not always return true when two numbers have the same numeric value.

What is a collection in Clojure?

Clojure collections "collect" values into compound values. There are four key Clojure collection types: vectors, lists, sets, and maps. Of those four collection types, vectors and lists are ordered.

What is a clojure vector?

Advertisements. A Vector is a collection of values indexed by contiguous integers. A vector is created by using the vector method in Clojure.


2 Answers

From the docstring for defrecord:

In addition, defrecord will define type-and-value-based =, and will defined Java .hashCode and .equals consistent with the contract for java.util.Map.

So, when using =, type is taken into account. You could use .equals instead:

user> (.equals titi {:a 1 :b 2})
true
like image 72
Justin Kramer Avatar answered Oct 14 '22 03:10

Justin Kramer


a PersistentArrayMap and a PersistentHashMap are conceptually the same - as the ArrayMap grows, it will automatically get converted to a HashMap for performance reasons. User-level code should generally not try to distinguish between the two.

A defrecord datatype, on the other hand, is not the same as one of the other maps. It is a separate type that may implement entirely different interfaces and should not be automatically replaced by some other form of map. It is not conceptually equal to a normal map, so = returns false.

like image 33
Retief Avatar answered Oct 14 '22 01:10

Retief