Last active
February 5, 2018 14:43
-
-
Save smee/4c6e95d0904c09e23154215a3b6d4c72 to your computer and use it in GitHub Desktop.
inital draft of a multi comparator, bug in the macro expansion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns multicompare) | |
;;; FIXME the ~key-fn expansion does not resolve all the symbols in `key-fn`, so advanced compilation in clojurescript fails! | |
(defn- multi-compare* [a b [[key-fn order] & key-fn-order-pairs]] | |
(let [c (gensym "compare-result-") | |
k (gensym "key-fn-") | |
compare-clause `(compare ~@(if (#{:asc :ASC :ascending :ASCENDING} order) | |
`[(~k ~a) (~k ~b)] | |
`[(~k ~b) (~k ~a)]))] | |
(if (nil? key-fn-order-pairs) | |
`(let [~k ~key-fn] | |
~compare-clause) | |
`(let [~k ~key-fn | |
~c ~compare-clause] | |
(if (zero? ~c) | |
~(multi-compare* a b key-fn-order-pairs) | |
~c))))) | |
(defmacro multi-compare | |
"Composite comparator for sorting collections | |
for different key functions in different sort orders. | |
Example: | |
(macroexpand-1 `(multi-compare [:foo :ASC :bar :DESC count :ASC])) | |
;==> | |
(fn [arg1 arg2] | |
(let [compare-result | |
(compare (:foo arg1) (:foo arg2))] | |
(if (zero? compare-result) | |
(let [compare-result | |
(compare (:bar arg2) (:bar arg1))] | |
(if (zero? compare-result) | |
(compare (count arg1) (count arg2)) | |
compare-result)) | |
compare-result))) | |
" | |
[& key-fn-order-pairs] | |
(assert (every? #(= 2 (count %)) key-fn-order-pairs)) | |
(assert (every? #{:ASC :DESC} (map second key-fn-order-pairs))) | |
(let [a (gensym "arg1-") | |
b (gensym "arg2-")] | |
`(fn [~a ~b] | |
~(multi-compare* a b key-fn-order-pairs)))) | |
(comment | |
;; example | |
(sort (multi-compare [:date :ASC] | |
[count :DESC] | |
[#(get % :foo) :DESC]) | |
[{:date 1 :foo 1 :a 1} | |
{:date 2 :foo 1 :a 1 :b 2} | |
{:date 2 :foo 1 :a 1 :b 2 :c 3} | |
{:date 1 :foo 2 :a 1} | |
{:date 6 :foo 2 :a 1 :b 2}]) | |
;; problem: in the exansion `count` and `fn*` from the anonymous functions have no namespace! | |
(clojure.pprint/pprint | |
(macroexpand-1 | |
'(multi-compare [:foo :ASC] | |
[:bar :DESC] | |
[count :ASC] | |
[#(get % "id") :DESC]))) | |
;; output: | |
(clojure.core/fn | |
[arg1-169175 arg2-169176] | |
(clojure.core/let | |
[key-fn-169178 | |
:foo | |
compare-result-169177 | |
(clojure.core/compare | |
(key-fn-169178 arg1-169175) | |
(key-fn-169178 arg2-169176))] | |
(if | |
(clojure.core/zero? compare-result-169177) | |
(clojure.core/let | |
[key-fn-169180 | |
:bar | |
compare-result-169179 | |
(clojure.core/compare | |
(key-fn-169180 arg2-169176) | |
(key-fn-169180 arg1-169175))] | |
(if | |
(clojure.core/zero? compare-result-169179) | |
(clojure.core/let | |
[key-fn-169182 | |
count | |
compare-result-169181 | |
(clojure.core/compare | |
(key-fn-169182 arg1-169175) | |
(key-fn-169182 arg2-169176))] | |
(if | |
(clojure.core/zero? compare-result-169181) | |
(clojure.core/let | |
[key-fn-169184 (fn* [p1__169172#] (get p1__169172# "id"))] | |
(clojure.core/compare | |
(key-fn-169184 arg2-169176) | |
(key-fn-169184 arg1-169175))) | |
compare-result-169181)) | |
compare-result-169179)) | |
compare-result-169177))) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment