 ;; We assume that if we rely on attr then stratification ensures no more retractions are forthcoming
 
 ;; runtime
+(defn with-info [f x]
+  (aset f "our_info" x)
+  f)
+
+(defn info [f]
+  (aget f "our_info"))
 
 (defrecord Knowledge [old asserted retracted])
 

    #{}))

(def empty-q
  (with-meta
    (fn [kn]
      #{{}})
    {::shape #{}}))

(defn debug-q [query]
  (with-meta
    (fn [kn]
      (let [facts (query kn)]
        (prn facts)
        facts))
    (meta query)))

(defn project-q [pattern kn-f]
  (let [shape (match/vars pattern)
        return-syms (into [] shape)
        f (match/pattern pattern return-syms)]
    (with-meta
      (fn [kn]
        (into #{}
              (for [fact (kn-f kn)
                    :let [vals (f fact)]

        group-f #(select-keys % project-syms)
        shape (conj (set project-syms) name-sym)
        gen (gen* clauses)]
    (with-info
      (fn set-q-fn [kn]
        (into #{}
              (for [[projects selects] (group-by group-f (gen kn))]
                (assoc (zipmap project-syms projects) name-sym (set (map #(select-keys % select-syms) selects))))))
      {::shape shape})))

(defn in-q [query name-sym set-sym]
  (fn in-q-fn [kn]
    (for [fact (query kn)
          elem (get fact set-sym :inq-not-found)]
      (assoc fact name-sym elem))))

(defn gen* [clauses]
  (reduce
   (fn gen*-fn [query clause]
     (condp op? clause
       '+ed (join-q query (project-q (second clause) :asserted))
       '-ed (join-q query (project-q (second clause) :retracted))
       '? (filter-q query (second clause))
       '= (let-q query (nth clause 1) (nth clause 2))
       'set (join-q query (set-q (nth clause 1) (nth clause 2) (nthnext clause 3)))
       'in (in-q query (nth clause 1) (nth clause 2))
       '+ query ;; handled later
       '+s query ;; handled later
       '- query ;; handled later
       '-s query ;; handled later
       '> query ;; handled later
       (join-q query (project-q clause to-be))))
   empty-q
   clauses))

