restructured in terms of CompiledPath and implemented composition for ParamsNeededPath

This commit is contained in:
Nathan Marz 2015-09-10 02:38:52 -04:00
parent d693ad29ae
commit 6e3f79dd53
2 changed files with 134 additions and 99 deletions

View file

@ -18,7 +18,7 @@
(defn select (defn select
"Navigates to and returns a sequence of all the elements specified by the selector." "Navigates to and returns a sequence of all the elements specified by the selector."
[selector structure] [selector structure]
(compiled-select (i/comp-unoptimal selector) (compiled-select (i/comp-paths* selector)
structure)) structure))
(defn compiled-select-one (defn compiled-select-one
@ -33,7 +33,7 @@
(defn select-one (defn select-one
"Like select, but returns either one element or nil. Throws exception if multiple elements found" "Like select, but returns either one element or nil. Throws exception if multiple elements found"
[selector structure] [selector structure]
(compiled-select-one (i/comp-unoptimal selector) structure)) (compiled-select-one (i/comp-paths* selector) structure))
(defn compiled-select-one! (defn compiled-select-one!
"Version of select-one! that takes in a selector pre-compiled with comp-paths" "Version of select-one! that takes in a selector pre-compiled with comp-paths"
@ -46,7 +46,7 @@
(defn select-one! (defn select-one!
"Returns exactly one element, throws exception if zero or multiple elements found" "Returns exactly one element, throws exception if zero or multiple elements found"
[selector structure] [selector structure]
(compiled-select-one! (i/comp-unoptimal selector) structure)) (compiled-select-one! (i/comp-paths* selector) structure))
(defn compiled-select-first (defn compiled-select-first
"Version of select-first that takes in a selector pre-compiled with comp-paths" "Version of select-first that takes in a selector pre-compiled with comp-paths"
@ -56,7 +56,7 @@
(defn select-first (defn select-first
"Returns first element found. Not any more efficient than select, just a convenience" "Returns first element found. Not any more efficient than select, just a convenience"
[selector structure] [selector structure]
(compiled-select-first (i/comp-unoptimal selector) structure)) (compiled-select-first (i/comp-paths* selector) structure))
;; Transformfunctions ;; Transformfunctions
@ -68,7 +68,7 @@
"Navigates to each value specified by the selector and replaces it by the result of running "Navigates to each value specified by the selector and replaces it by the result of running
the transform-fn on it" the transform-fn on it"
[selector transform-fn structure] [selector transform-fn structure]
(compiled-transform (i/comp-unoptimal selector) transform-fn structure)) (compiled-transform (i/comp-paths* selector) transform-fn structure))
(defn compiled-setval (defn compiled-setval
"Version of setval that takes in a selector pre-compiled with comp-paths" "Version of setval that takes in a selector pre-compiled with comp-paths"
@ -78,7 +78,7 @@
(defn setval (defn setval
"Navigates to each value specified by the selector and replaces it by val" "Navigates to each value specified by the selector and replaces it by val"
[selector val structure] [selector val structure]
(compiled-setval (i/comp-unoptimal selector) val structure)) (compiled-setval (i/comp-paths* selector) val structure))
(defn compiled-replace-in (defn compiled-replace-in
"Version of replace-in that takes in a selector pre-compiled with comp-paths" "Version of replace-in that takes in a selector pre-compiled with comp-paths"
@ -106,7 +106,7 @@
in the final return. replace-in is useful for situations where you need to know the specific values in the final return. replace-in is useful for situations where you need to know the specific values
of what was transformd in the data structure." of what was transformd in the data structure."
[selector transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}] [selector transform-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
(compiled-replace-in (i/comp-unoptimal selector) transform-fn structure :merge-fn merge-fn)) (compiled-replace-in (i/comp-paths* selector) transform-fn structure :merge-fn merge-fn))
;; Built-in pathing and context operations ;; Built-in pathing and context operations

View file

@ -38,13 +38,13 @@
(def RichPathExecutor (def RichPathExecutor
(->ExecutorFunctions (->ExecutorFunctions
:richpath :richpath
(fn [selector structure] (fn [params selector structure]
(selector [] structure (selector params 0 [] structure
(fn [vals structure] (fn [params params-idx vals structure]
(if-not (empty? vals) [(conj vals structure)] [structure])))) (if-not (empty? vals) [(conj vals structure)] [structure]))))
(fn [transformer transform-fn structure] (fn [params transformer transform-fn structure]
(transformer [] structure (transformer params 0 [] structure
(fn [vals structure] (fn [params params-idx vals structure]
(if (empty? vals) (if (empty? vals)
(transform-fn structure) (transform-fn structure)
(apply transform-fn (conj vals structure)))))) (apply transform-fn (conj vals structure))))))
@ -53,17 +53,18 @@
(def StructurePathExecutor (def StructurePathExecutor
(->ExecutorFunctions (->ExecutorFunctions
:spath :spath
(fn [selector structure] (fn [params selector structure]
(selector structure (fn [structure] [structure]))) (selector structure (fn [structure] [structure])))
(fn [transformer transform-fn structure] (fn [params transformer transform-fn structure]
(transformer structure transform-fn)) (transformer structure transform-fn))
)) ))
(deftype TransformFunctions [executors selector transformer]) (defrecord TransformFunctions [executors selector transformer])
(defrecord CompiledPath [transform-fns params])
(defprotocol CoerceTransformFunctions ;;TODO: this must implement IFn so it can be transformed to CompiledPath
(coerce-path [this])) (defrecord ParamsNeededPath [transform-fns num-needed-params])
(defn- seq-contains? [aseq val] (defn- seq-contains? [aseq val]
(->> aseq (->> aseq
@ -119,48 +120,61 @@
collector-impl collector-impl
:collect-val :collect-val
) )
afn (fn [vals structure next-fn] afn (fn [params params-idx vals structure next-fn]
(next-fn (conj vals (cfn this structure)) structure) (next-fn params params-idx (conj vals (cfn this structure)) structure)
)] )]
(->TransformFunctions RichPathExecutor afn afn))) (->CompiledPath
(->TransformFunctions RichPathExecutor afn afn)
nil
)))
(defn coerce-structure-path [this] (defn coerce-structure-path [this]
(let [pimpl (structure-path-impl this) (let [pimpl (structure-path-impl this)
selector (:select* pimpl) selector (:select* pimpl)
transformer (:transform* pimpl)] transformer (:transform* pimpl)]
(->CompiledPath
(->TransformFunctions (->TransformFunctions
StructurePathExecutor StructurePathExecutor
(fn [structure next-fn] (fn [structure next-fn]
(selector this structure next-fn)) (selector this structure next-fn))
(fn [structure next-fn] (fn [structure next-fn]
(transformer this structure next-fn)) (transformer this structure next-fn)))
nil
))) )))
(defn coerce-structure-path-direct [this] (defn coerce-structure-path-rich [this]
(let [pimpl (structure-path-impl this) (let [pimpl (structure-path-impl this)
selector (:select* pimpl) selector (:select* pimpl)
transformer (:transform* pimpl)] transformer (:transform* pimpl)]
(->CompiledPath
(->TransformFunctions (->TransformFunctions
RichPathExecutor RichPathExecutor
(fn [vals structure next-fn] (fn [params params-idx vals structure next-fn]
(selector this structure (fn [structure] (next-fn vals structure)))) (selector this structure (fn [structure] (next-fn params params-idx vals structure))))
(fn [vals structure next-fn] (fn [params params-idx vals structure next-fn]
(transformer this structure (fn [structure] (next-fn vals structure)))) (transformer this structure (fn [structure] (next-fn params params-idx vals structure)))))
nil
))) )))
(defn structure-path? [obj] (defn structure-path? [obj]
(or (fn? obj) (satisfies? p/StructurePath obj))) (or (fn? obj) (satisfies? p/StructurePath obj)))
(extend-protocol CoerceTransformFunctions (defprotocol CoercePath
(coerce-path [this]))
(extend-protocol CoercePath
nil ; needs its own path because it doesn't count as an Object nil ; needs its own path because it doesn't count as an Object
(coerce-path [this] (coerce-path [this]
(coerce-structure-path nil)) (coerce-structure-path nil))
TransformFunctions CompiledPath
(coerce-path [this] (coerce-path [this]
this) this)
ParamsNeededPath
(coerce-path [this]
this)
#?(:clj java.util.List :cljs cljs.core/PersistentVector) #?(:clj java.util.List :cljs cljs.core/PersistentVector)
(coerce-path [this] (coerce-path [this]
@ -192,8 +206,6 @@
)) ))
(defn- combine-same-types [[^TransformFunctions f & _ :as all]] (defn- combine-same-types [[^TransformFunctions f & _ :as all]]
(if (empty? all)
(coerce-path nil)
(let [^ExecutorFunctions exs (.-executors f) (let [^ExecutorFunctions exs (.-executors f)
t (.-type exs) t (.-type exs)
@ -201,10 +213,10 @@
combiner combiner
(if (= t :richpath) (if (= t :richpath)
(fn [curr next] (fn [curr next]
(fn [vals structure next-fn] (fn [params params-idx vals structure next-fn]
(curr vals structure (curr params params-idx vals structure
(fn [vals-next structure-next] (fn [params-next params-idx-next vals-next structure-next]
(next vals-next structure-next next-fn) (next params-next params-idx-next vals-next structure-next next-fn)
)))) ))))
(fn [curr next] (fn [curr next]
(fn [structure next-fn] (fn [structure next-fn]
@ -217,21 +229,44 @@
(combiner (.-selector curr) (.-selector next)) (combiner (.-selector curr) (.-selector next))
(combiner (.-transformer curr) (.-transformer next)) (combiner (.-transformer curr) (.-transformer next))
)) ))
all)))) all)))
(defn coerce-structure-vals [^TransformFunctions tfns] (defn coerce-tfns-rich [^TransformFunctions tfns]
(if (= (extype tfns) :richpath) (if (= (extype tfns) :richpath)
tfns tfns
(let [selector (.-selector tfns) (let [selector (.-selector tfns)
transformer (.-transformer tfns)] transformer (.-transformer tfns)]
(->TransformFunctions (->TransformFunctions
RichPathExecutor RichPathExecutor
(fn [vals structure next-fn] (fn [params params-idx vals structure next-fn]
(selector structure (fn [structure] (next-fn vals structure)))) (selector structure (fn [structure] (next-fn params params-idx vals structure))))
(fn [vals structure next-fn] (fn [params params-idx vals structure next-fn]
(transformer structure (fn [structure] (next-fn vals structure)))) (transformer structure (fn [structure] (next-fn params params-idx vals structure))))
)))) ))))
(defn capture-params-internally [path]
(if-not (instance? CompiledPath path)
path
(let [params (:params path)
selector (-> path :transform-fns :selector)
transformer (-> path :transform-fns :transformer)]
(if (empty? params)
path
(->CompiledPath
(->TransformFunctions
RichPathExecutor
(fn [x-params params-idx vals structure next-fn]
(selector params 0 vals structure
(fn [_ _ vals-next structure-next]
(next-fn x-params params-idx vals-next structure-next)
)))
(fn [x-params params-idx vals structure next-fn]
(transformer params 0 vals structure
(fn [_ _ vals-next structure-next]
(next-fn x-params params-idx vals-next structure-next)
)))
))))))
(extend-protocol PathComposer (extend-protocol PathComposer
nil nil
(comp-paths* [sp] (comp-paths* [sp]
@ -241,34 +276,32 @@
(coerce-path sp)) (coerce-path sp))
#?(:clj java.util.List :cljs cljs.core/PersistentVector) #?(:clj java.util.List :cljs cljs.core/PersistentVector)
(comp-paths* [structure-paths] (comp-paths* [structure-paths]
(let [combined (->> structure-paths (if (empty? structure-paths)
(coerce-path nil)
(let [coerced (->> structure-paths
(map coerce-path) (map coerce-path)
(map capture-params-internally))
combined (->> coerced
(map :transform-fns)
(partition-by extype) (partition-by extype)
(map combine-same-types) (map combine-same-types)
)] )
(if (= 1 (count combined)) result-tfn (if (= 1 (count combined))
(first combined) (first combined)
(->> combined (->> combined
(map coerce-structure-vals) (map coerce-tfns-rich)
combine-same-types) combine-same-types)
)))) )
needs-params-paths (filter #(instance? ParamsNeededPath %) coerced)]
(defn coerce-structure-vals-direct [this] (if (empty? needs-params-paths)
(cond (structure-path? this) (coerce-structure-path-direct this) (->CompiledPath result-tfn nil)
(satisfies? p/Collector this) (coerce-collector this) (->ParamsNeededPath
(instance? TransformFunctions this) (coerce-structure-vals this) (coerce-tfns-rich result-tfn)
:else (throw-illegal (no-prot-error-str this)) (->> needs-params-paths
(map :num-needed-params)
(reduce +))
)) ))
))))
;;this composes paths together much faster than comp-paths* but the resulting composition
;;won't execute as fast. Useful for when select/transform are used without pre-compiled paths
;;(where cost of compiling dominates execution time)
(defn comp-unoptimal [sp]
(if (instance? #?(:clj java.util.List :cljs cljs.core/PersistentVector) sp)
(->> sp
(map coerce-structure-vals-direct)
combine-same-types)
(coerce-path sp)))
;; cell implementation idea taken from prismatic schema library ;; cell implementation idea taken from prismatic schema library
(defprotocol PMutableCell (defprotocol PMutableCell
@ -346,15 +379,17 @@
(set-cell! cell (concat (get-cell cell) elems))) (set-cell! cell (concat (get-cell cell) elems)))
(defn compiled-select* (defn compiled-select*
[^com.rpl.specter.impl.TransformFunctions tfns structure] [^com.rpl.specter.impl.CompiledPath path structure]
(let [^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)] (let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
((.-select-executor ex) (.-selector tfns) structure) ^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
((.-select-executor ex) (.-params path) (.-selector tfns) structure)
)) ))
(defn compiled-transform* (defn compiled-transform*
[^com.rpl.specter.impl.TransformFunctions tfns transform-fn structure] [^com.rpl.specter.impl.CompiledPath path transform-fn structure]
(let [^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)] (let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
((.-transform-executor ex) (.-transformer tfns) transform-fn structure) ^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
((.-transform-executor ex) (.-params path) (.-transformer tfns) transform-fn structure)
)) ))
(defn selected?* (defn selected?*