How does clojure.core / compare implement java.util.Comparator?
I saw this code in clojure.core recently.
(defn sort-by
"Returns a sorted sequence of the items in coll, where the sort
order is determined by comparing (keyfn item). If no comparator is
supplied, uses compare. comparator must implement
java.util.Comparator. If coll is a Java array, it will be modified.
To avoid this, sort a copy of the array."
{:added "1.0"
:static true}
([keyfn coll]
(sort-by keyfn compare coll))
([keyfn ^java.util.Comparator comp coll]
(sort (fn [x y] (. comp (compare (keyfn x) (keyfn y)))) coll)))
There is a type hint Comparator
for the argument comp
. But two version arguments sort-by
are passed to it clojure.core/compare
. How it works?
Update:
I would like to know how it clojure.core/compare
implements java.util.Comparator
. compare
as follows:
(defn compare
"Comparator. Returns a negative number, zero, or a positive number
when x is logically 'less than', 'equal to', or 'greater than'
y. Same as Java x.compareTo(y) except it also works for nil, and
compares numbers and collections in a type-independent manner. x
must implement Comparable"
{
:inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))
:added "1.0"}
[x y] (. clojure.lang.Util (compare x y)))
Isn't this a normal clojure function?
source to share
From jvm/clojure/lang/AFunction.java
:
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {
/* ...omitted... */
public int compare(Object o1, Object o2){
Object o = invoke(o1, o2);
if(o instanceof Boolean)
{
if(RT.booleanCast(o))
return -1;
return RT.booleanCast(invoke(o2,o1))? 1 : 0;
}
Number n = (Number) o;
return n.intValue();
}
}
When the Clojure compiler compiles functions, it either implements them as derivatives of RestFn (if variable) or AFunction (otherwise); however RestFn extends AFunction so everything ends up at the same place.
So: all Clojure functions implement Comparator through AFunction, directly or indirectly.
source to share
UPDATE . Below is my answer based on my confusion as to what was asked: I thought the question was about 3-arity overload instead of 2-arity overload.
I think the confusion comes from the phrase "two arguments of the sort-pass version go clojure.core/compare
to it". This is not true. Look at the code:
(. comp (compare (keyfn x) (keyfn y)))
It uses the "exact special form" (see .
as the first item in the list). It is used here as a method call. It will call the method compare
on the instance provided comp
with arguments (keyfn x) (keyfn y)
. clojure.core/compare
There's nothing to do here. Of the various forms of dot expressions, this corresponds to the following case:
(. instance-expr (method-symbol args*))
About the type hint: This is just a performance optimization to avoid being reflected in the method call.
source to share