So I am working on some reflective methods for datomic - in particular I want to be able to access the various parts of the query as a map.
Datomic queries take the form of clojure vectors containing keywords and symbols/vectors e.g.
[:kw ?x ?y :kw1 [?x is ?y]]
Based on that I want to turn that into a map so I can access the parts of the query via :kw and :kw1
Here is my first stab at such a function:
(defn query-info [query]
(let [parts (partition-by #(keyword? %) query)
is-kw (fn [x] (keyword? (first x)))
q-keys (map first (filter is-kw parts))
q-vals (map #(into [] %) (filter #(not (is-kw %)) parts))]
(zipmap q-keys q-vals)))
Just thought I would share an example of why lisp rules - being able to take someone elses DSL and write a simple introspection method on the syntax w/ minimal fuss.
From this I now have a wrapper function for queries which returns maps instead of vectors (so you can access them via key instead of position)
Originally I was doing:
(defn key-query [query & args]
(let [find-keywords (map #(keyword (subs (str %) 1))
(first (split-with #(not (keyword? %))
result (apply q (apply conj [query] args))]
(map #(zipmap find-keywords %) result)))
But I realized this was brittle in that it depends on the :find clause being the first part of the query. This is not a rule and just a convention thus the (query-info) function which does not depend on position but rather builds a map for each portion of the query.
So now:
(defn key-query [query & args]
(let [find-keywords (map #(keyword (subs (str %) 1))
(:find (query-info query)))
result (apply q (apply conj [query] args))]
(map #(zipmap find-keywords %) result)))
Notice the trick of turning the symbols into keywords via subs.
Anyway that is all for my survivor man driven development journal entry. -shaun