Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Collections by

The Util.pcequiv() method

`
static public boolean pcequiv(Object k1, Object k2){

if(k1 instanceof IPersistentCollection)
	return ((IPersistentCollection)k1).equiv(k2);
return ((IPersistentCollection)k2).equiv(k1);

}
`

tries to get equiv semantics (cross-class number equality) for cases of mixed Clojure/Java collection comparison. However, this is not a sustainable direction and we would like to stop doing this.

Attached patch removes this and changes calling code to only call equiv when both collections are IPersistentCollection.

1 Answer

+1 vote
by
Reference: https://clojure.atlassian.net/browse/CLJ-1375 (reported by alexmiller)
by
I actually hit corner case today involving core.cache and raxod502/lazy-map. The lazy map implementation defines a type implementing clojure.lang.IPersistentCollection and its equiv method (by calling .equiv on current object forced to be realized as regular map).

This may require a fix in lazy-map anyway but the reason it was happening was the lazy map was used as value associated with some key in a cache, and clojure.core.cache.wrapped/lookup-or-miss when comparing the whole map to a special value ::expired (a keyword) uses = function. The clojure.core/= dispatches  to clojure.lang.Util/equiv which calls clojure.lang.Util/pcequiv As a result, the whole map hold in cache as one of the values is immediately realized (current values of Delays are calculated) in order to get the actual content of it - even if it is compared to a keyword!

Workarounds/resolutions are possible in lazy map implementation (by checking if both compared objects are actually collections), in core.cache (by using identical? instead of = to check if a value in a loop is a specific keyword) or by applying this patch.
by
I'm not sure that makes sense to me - is one of the values being compared a Java non-Clojure collection?
by
1. LazyMap object (implementing IPersistentCollection) is compared to a keyword using clojure.core/=

2. clojure.core/= calls clojure.lang.Util/equiv

3.  clojure.lang.Util/equiv calls  clojure.lang.Util/pcequiv

4. pcequiv calls equiv from IPersistentCollection implementation for LazyMap causing all delayed values to be realized (which should be since it's required to fully compare two maps), even if the other object is a keyword (used in a quite idiomatic way to communicate some special condition, in this case :clojure.core.cache.wrapped/expired).
by
The ticket in question here is about comparing Java collections to Clojure collections, so I don't think your case is covered by that, other than by pure implementation mechanism.
...