I noticed that Clojure (1.4) seems to be happy to consider vectors equal to the seq
of the same vector, but that the same does not apply for maps:
(= [1 2] (seq [1 2]))
=> true
(= {1 2} (seq {1 2}))
=> false
Why should the behaviour of =
be different in this way?
Clojure's
=
can be thought of as performing its comparisons in two steps:Check if the types of the things being compared belong to the same "equality partition", that is a class of types whose members might potentially be equal (depending on things like the exact members of a given data structure, but not the particular type in the partition);
If so, check if the things being compared actually are equal.
One such equality partition is that of "sequential" things. Vectors are considered sequential:
As are seqs of various types:
Therefore a vector is considered equal to a seq if (and only if) their corresponding elements are equal.
(Note that
(seq {})
producesnil
, which is not sequential and compares "not equal" to()
,[]
etc.)On the other hand, maps constitute an equality partition of their own, so while a hash map might be considered equal to a sorted map, it will never be considered equal to a seq. In particular, it is not equal to the seq of its entries, which is what
(seq some-map)
produces.I guess this is because in sequences order as well as value at particular position matters where as in map the order of key/value doesn't matter and this difference between semantics causes this to work as shown by your sample code.
For more details have a look at
mapEquals
in file https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.javaIt checks if the other object is not map then return false.
It seems to me that this example points out a slight inconsistency in the notion of equality of values in clojure for this case where they are different types derived from the same type (by the
seq
function). It could well be argued that this is not inconsistent because it is comparing a derived type to the type it is derived from and I can understand that if the same logic was applied to this same example using vectors (note at the bottom)the contents are the same type:
the sequences have the same values
but they are not considered equal user> (= {1 2} (seq {1 2})) false
this is true for longer maps as well:
even if they are not in the same order:
but again not if the containing types differ :-(
EDIT: this is not always true see below: to work around this you can convert everything to a seq before comparison, which is (I presume) safe because the seq function always iterates the whole data structure the same way and the structures are immutable values and a
seq
of aseq
is aseq
vectors are treated differently:
Perhaps understanding the minor inconsistencies is part of learning a language or someday this could be changed (though I would not hold my breath)
EDIT:
I found two maps that produce different sequences for the same value so just calling seq on the maps will not give you proper map equality:
here is an example of what I'm calling proper map equality:
(seq some-hash-map)
gives you a sequence of entries (key/value pairs).For example:
which is not the same as
[:a 1 :b 2 :c 3]
.