Search code examples
clojuretype-hintingclojure-java-interop

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 Comparator type hint on the argument comp. But the two argument version of sort-by passes clojure.core/compare to it. How does this work?

Update:

I would like to know how clojure.core/compare implements java.util.Comparator. compare looks like this:

(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)))

Isnt't this just a normal clojure function?


Solution

  • 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 is compiling functions, it either implements them as derivatives of RestFn (if variadic) or AFunction (otherwise); however, RestFn extends AFunction, so it all ends up at the same place.

    So: All Clojure functions implement Comparator through AFunction, either directly or indirectly.