paramspath working with composition, measured about 14% slower than compiled path without params and 15x faster than select with on the fly compilation
This commit is contained in:
parent
6e3f79dd53
commit
d8feed2ca1
2 changed files with 188 additions and 21 deletions
|
|
@ -108,6 +108,55 @@
|
||||||
[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-paths* selector) transform-fn structure :merge-fn merge-fn))
|
(compiled-replace-in (i/comp-paths* selector) transform-fn structure :merge-fn merge-fn))
|
||||||
|
|
||||||
|
(def bind-params i/bind-params)
|
||||||
|
|
||||||
|
;; paramspath* [bindings num-params-sym [impl1 impl2]]
|
||||||
|
|
||||||
|
(defmacro paramspath [params & impls]
|
||||||
|
(let [num-params (count params)
|
||||||
|
retrieve-params (->> params
|
||||||
|
(map-indexed
|
||||||
|
(fn [i p]
|
||||||
|
[p `(aget ~i/PARAMS-SYM
|
||||||
|
(+ ~i/PARAMS-IDX-SYM ~i))]
|
||||||
|
))
|
||||||
|
(apply concat))]
|
||||||
|
(i/paramspath* retrieve-params num-params impls)
|
||||||
|
))
|
||||||
|
|
||||||
|
(defmacro defparamspath [name & body]
|
||||||
|
`(def ~name (paramspath ~@body)))
|
||||||
|
|
||||||
|
(defmacro params-paramspath [bindings & impls]
|
||||||
|
(let [quoted-bindings (->> bindings
|
||||||
|
(partition 2)
|
||||||
|
(map (fn [[sym path-sym]]
|
||||||
|
[`(quote ~sym) `(quote ~(gensym "path")) path-sym]
|
||||||
|
)))]
|
||||||
|
`(i/params-paramspath* ~quoted-bindings (quote ~impls))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(defn filterer [& path]
|
||||||
|
(let [path (i/comp-paths* path)]
|
||||||
|
(params-paramspath [late path]
|
||||||
|
(select* [this structure next-fn]
|
||||||
|
;; same code
|
||||||
|
)
|
||||||
|
(transform* [this structure next-fn]
|
||||||
|
;; same code
|
||||||
|
))))
|
||||||
|
|
||||||
|
|
||||||
|
;;TODO: figure out how to express higher order selectors like filterer, selected?, cond-path
|
||||||
|
;; - if keep params-idx in compiledpath too, then:
|
||||||
|
;; - needs to emit paramsneeded if it needs params
|
||||||
|
;; - at runtime, it converts internal selector into CompiledPath with
|
||||||
|
;; the current params/params-idx
|
||||||
|
;;TODO: figure out how to express srange in terms of srange-dynamic
|
||||||
|
;; - will need selector and transformer to call into shared functions
|
||||||
|
;;TODO: get rid of KeyPath
|
||||||
|
|
||||||
;; Built-in pathing and context operations
|
;; Built-in pathing and context operations
|
||||||
|
|
||||||
(def ALL (i/->AllStructurePath))
|
(def ALL (i/->AllStructurePath))
|
||||||
|
|
@ -118,8 +167,10 @@
|
||||||
|
|
||||||
(def FIRST (i/->PosStructurePath first i/set-first))
|
(def FIRST (i/->PosStructurePath first i/set-first))
|
||||||
|
|
||||||
|
;;TODO: should be parameterized
|
||||||
(defn srange-dynamic [start-fn end-fn] (i/->SRangePath start-fn end-fn))
|
(defn srange-dynamic [start-fn end-fn] (i/->SRangePath start-fn end-fn))
|
||||||
|
|
||||||
|
;;TODO: should be parameterized
|
||||||
(defn srange [start end] (srange-dynamic (fn [_] start) (fn [_] end)))
|
(defn srange [start end] (srange-dynamic (fn [_] start) (fn [_] end)))
|
||||||
|
|
||||||
(def BEGINNING (srange 0 0))
|
(def BEGINNING (srange 0 0))
|
||||||
|
|
@ -130,9 +181,33 @@
|
||||||
|
|
||||||
(defn codewalker [afn] (i/->CodeWalkerStructurePath afn))
|
(defn codewalker [afn] (i/->CodeWalkerStructurePath afn))
|
||||||
|
|
||||||
|
;;TODO: needs to parameterize if necessary according to its path
|
||||||
|
;; same for selected?, not-selected?, transformed, collect, collect-one,
|
||||||
|
;; cond-path, multi-path
|
||||||
|
;; TODO: but should only become a late bound object if its internal path
|
||||||
|
;; is parameterized
|
||||||
|
;; want an interface that gives regular structure path interface but
|
||||||
|
;; creates the right thing
|
||||||
|
|
||||||
|
; (higherorderparamspath [late1 path1
|
||||||
|
; late2 path2
|
||||||
|
; late3 path3]
|
||||||
|
; (select* [this structure next-fn]
|
||||||
|
; (compiled-select late1 ...)
|
||||||
|
; ;;TODO: if its multiple paths... where to do the index manipulation...?
|
||||||
|
; ;;could take in another arg of "latebound paths" that can then be used internally...
|
||||||
|
; ;; but if nothing was higher order, then its just direct
|
||||||
|
; ;; this never directly accesses params
|
||||||
|
; ))
|
||||||
|
|
||||||
(defn filterer [& path] (i/->FilterStructurePath (i/comp-paths* path)))
|
(defn filterer [& path] (i/->FilterStructurePath (i/comp-paths* path)))
|
||||||
|
|
||||||
(defn keypath [akey] (i/->KeyPath akey))
|
(defparamspath keypath [key]
|
||||||
|
(select* [this structure next-fn]
|
||||||
|
(next-fn (get structure key)))
|
||||||
|
(transform* [this structure next-fn]
|
||||||
|
(assoc structure key (next-fn (get structure key)))
|
||||||
|
))
|
||||||
|
|
||||||
(defn view [afn] (i/->ViewPath afn))
|
(defn view [afn] (i/->ViewPath afn))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,13 @@
|
||||||
[clojure.string :as s])
|
[clojure.string :as s])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(def ^:dynamic *tmp-closure*)
|
||||||
|
(defn closed-code [closure body]
|
||||||
|
(let [lv (mapcat #(vector % `(*tmp-closure* '~%))
|
||||||
|
(keys closure))]
|
||||||
|
(binding [*tmp-closure* closure]
|
||||||
|
(eval `(let [~@lv] ~body)))))
|
||||||
|
|
||||||
(defprotocol PathComposer
|
(defprotocol PathComposer
|
||||||
(comp-paths* [paths]))
|
(comp-paths* [paths]))
|
||||||
|
|
||||||
|
|
@ -38,13 +45,13 @@
|
||||||
(def RichPathExecutor
|
(def RichPathExecutor
|
||||||
(->ExecutorFunctions
|
(->ExecutorFunctions
|
||||||
:richpath
|
:richpath
|
||||||
(fn [params selector structure]
|
(fn [params params-idx selector structure]
|
||||||
(selector params 0 [] structure
|
(selector params params-idx [] structure
|
||||||
(fn [params params-idx vals structure]
|
(fn [_ _ vals structure]
|
||||||
(if-not (empty? vals) [(conj vals structure)] [structure]))))
|
(if-not (empty? vals) [(conj vals structure)] [structure]))))
|
||||||
(fn [params transformer transform-fn structure]
|
(fn [params params-idx transformer transform-fn structure]
|
||||||
(transformer params 0 [] structure
|
(transformer params params-idx [] structure
|
||||||
(fn [params params-idx vals structure]
|
(fn [_ _ 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,19 +60,30 @@
|
||||||
(def StructurePathExecutor
|
(def StructurePathExecutor
|
||||||
(->ExecutorFunctions
|
(->ExecutorFunctions
|
||||||
:spath
|
:spath
|
||||||
(fn [params selector structure]
|
(fn [params params-idx selector structure]
|
||||||
(selector structure (fn [structure] [structure])))
|
(selector structure (fn [structure] [structure])))
|
||||||
(fn [params transformer transform-fn structure]
|
(fn [params params-idx transformer transform-fn structure]
|
||||||
(transformer structure transform-fn))
|
(transformer structure transform-fn))
|
||||||
))
|
))
|
||||||
|
|
||||||
(defrecord TransformFunctions [executors selector transformer])
|
(defrecord TransformFunctions [executors selector transformer])
|
||||||
|
|
||||||
(defrecord CompiledPath [transform-fns params])
|
(defrecord CompiledPath [transform-fns params params-idx])
|
||||||
|
|
||||||
|
(defn no-params-compiled-path [transform-fns]
|
||||||
|
(->CompiledPath transform-fns nil 0))
|
||||||
|
|
||||||
;;TODO: this must implement IFn so it can be transformed to CompiledPath
|
;;TODO: this must implement IFn so it can be transformed to CompiledPath
|
||||||
|
;; (just calls bind-params)
|
||||||
(defrecord ParamsNeededPath [transform-fns num-needed-params])
|
(defrecord ParamsNeededPath [transform-fns num-needed-params])
|
||||||
|
|
||||||
|
|
||||||
|
(defn bind-params [^ParamsNeededPath params-needed-path params idx]
|
||||||
|
(->CompiledPath
|
||||||
|
(.-transform-fns params-needed-path)
|
||||||
|
params
|
||||||
|
idx))
|
||||||
|
|
||||||
(defn- seq-contains? [aseq val]
|
(defn- seq-contains? [aseq val]
|
||||||
(->> aseq
|
(->> aseq
|
||||||
(filter (partial = val))
|
(filter (partial = val))
|
||||||
|
|
@ -123,9 +141,8 @@
|
||||||
afn (fn [params params-idx vals structure next-fn]
|
afn (fn [params params-idx vals structure next-fn]
|
||||||
(next-fn params params-idx (conj vals (cfn this structure)) structure)
|
(next-fn params params-idx (conj vals (cfn this structure)) structure)
|
||||||
)]
|
)]
|
||||||
(->CompiledPath
|
(no-params-compiled-path
|
||||||
(->TransformFunctions RichPathExecutor afn afn)
|
(->TransformFunctions RichPathExecutor afn afn)
|
||||||
nil
|
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -133,28 +150,26 @@
|
||||||
(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
|
(no-params-compiled-path
|
||||||
(->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-rich [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
|
(no-params-compiled-path
|
||||||
(->TransformFunctions
|
(->TransformFunctions
|
||||||
RichPathExecutor
|
RichPathExecutor
|
||||||
(fn [params params-idx vals structure next-fn]
|
(fn [params params-idx vals structure next-fn]
|
||||||
(selector this structure (fn [structure] (next-fn params params-idx vals structure))))
|
(selector this structure (fn [structure] (next-fn params params-idx vals structure))))
|
||||||
(fn [params params-idx vals structure next-fn]
|
(fn [params params-idx vals structure next-fn]
|
||||||
(transformer this structure (fn [structure] (next-fn params params-idx vals structure)))))
|
(transformer this structure (fn [structure] (next-fn params params-idx vals structure)))))
|
||||||
nil
|
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(defn structure-path? [obj]
|
(defn structure-path? [obj]
|
||||||
|
|
@ -264,8 +279,10 @@
|
||||||
(transformer params 0 vals structure
|
(transformer params 0 vals structure
|
||||||
(fn [_ _ vals-next structure-next]
|
(fn [_ _ vals-next structure-next]
|
||||||
(next-fn x-params params-idx vals-next structure-next)
|
(next-fn x-params params-idx vals-next structure-next)
|
||||||
)))
|
))))
|
||||||
))))))
|
params
|
||||||
|
0
|
||||||
|
)))))
|
||||||
|
|
||||||
(extend-protocol PathComposer
|
(extend-protocol PathComposer
|
||||||
nil
|
nil
|
||||||
|
|
@ -294,7 +311,7 @@
|
||||||
)
|
)
|
||||||
needs-params-paths (filter #(instance? ParamsNeededPath %) coerced)]
|
needs-params-paths (filter #(instance? ParamsNeededPath %) coerced)]
|
||||||
(if (empty? needs-params-paths)
|
(if (empty? needs-params-paths)
|
||||||
(->CompiledPath result-tfn nil)
|
(no-params-compiled-path result-tfn)
|
||||||
(->ParamsNeededPath
|
(->ParamsNeededPath
|
||||||
(coerce-tfns-rich result-tfn)
|
(coerce-tfns-rich result-tfn)
|
||||||
(->> needs-params-paths
|
(->> needs-params-paths
|
||||||
|
|
@ -303,6 +320,81 @@
|
||||||
))
|
))
|
||||||
))))
|
))))
|
||||||
|
|
||||||
|
|
||||||
|
;; parameterized path helpers
|
||||||
|
|
||||||
|
(defn determine-params-impls [[name1 & impl1] [name2 & impl2]]
|
||||||
|
(if (= name1 'select*)
|
||||||
|
[impl1 impl2]
|
||||||
|
[impl2 impl1]))
|
||||||
|
|
||||||
|
|
||||||
|
(def PARAMS-SYM (vary-meta (gensym "params") assoc :tag 'objects))
|
||||||
|
(def PARAMS-IDX-SYM (gensym "params-idx"))
|
||||||
|
|
||||||
|
(defn paramspath* [bindings num-params [impl1 impl2]]
|
||||||
|
(let [[[[_ s-structure-sym s-next-fn-sym] & select-body]
|
||||||
|
[[_ t-structure-sym t-next-fn-sym] & transform-body]]
|
||||||
|
(determine-params-impls impl1 impl2)
|
||||||
|
|
||||||
|
params-sym (gensym "params")
|
||||||
|
params-idx-sym (gensym "params-idx")]
|
||||||
|
`(->ParamsNeededPath
|
||||||
|
(->TransformFunctions
|
||||||
|
RichPathExecutor
|
||||||
|
(fn [~PARAMS-SYM ~PARAMS-IDX-SYM vals# ~s-structure-sym next-fn#]
|
||||||
|
(let [~s-next-fn-sym (fn [structure#]
|
||||||
|
(next-fn#
|
||||||
|
~PARAMS-SYM
|
||||||
|
(+ ~PARAMS-IDX-SYM ~num-params)
|
||||||
|
vals#
|
||||||
|
structure#))
|
||||||
|
~@bindings]
|
||||||
|
~@select-body
|
||||||
|
))
|
||||||
|
(fn [~PARAMS-SYM ~PARAMS-IDX-SYM vals# ~t-structure-sym next-fn#]
|
||||||
|
(let [~t-next-fn-sym (fn [structure#]
|
||||||
|
(next-fn#
|
||||||
|
~PARAMS-SYM
|
||||||
|
(+ ~PARAMS-IDX-SYM ~num-params)
|
||||||
|
vals#
|
||||||
|
structure#))
|
||||||
|
~@bindings]
|
||||||
|
~@transform-body
|
||||||
|
)))
|
||||||
|
~num-params
|
||||||
|
)))
|
||||||
|
|
||||||
|
(defn num-needed-params [path]
|
||||||
|
(if (instance? CompiledPath path)
|
||||||
|
0
|
||||||
|
(:num-needed-params path)))
|
||||||
|
|
||||||
|
(defn params-paramspath* [bindings impls]
|
||||||
|
(let [num-params-seq (->> bindings
|
||||||
|
(map last)
|
||||||
|
(map num-needed-params)
|
||||||
|
(reductions +)
|
||||||
|
(cons 0))
|
||||||
|
num-params (last num-params-seq)
|
||||||
|
closure (->> bindings (map rest) (into {}))
|
||||||
|
make-paths (->> bindings
|
||||||
|
(map (fn [offset [late-sym path-sym path]]
|
||||||
|
[late-sym
|
||||||
|
(if (instance? CompiledPath path)
|
||||||
|
path-sym
|
||||||
|
`(bind-params ~path-sym ~PARAMS-SYM (+ ~PARAMS-IDX-SYM ~offset))
|
||||||
|
)
|
||||||
|
])
|
||||||
|
num-params-seq)
|
||||||
|
(apply concat))
|
||||||
|
_ (println "CLOSURE:" closure)
|
||||||
|
params-needed-path (closed-code closure (paramspath* make-paths num-params impls))]
|
||||||
|
(if (= num-params 0)
|
||||||
|
(bind-params params-needed-path nil 0)
|
||||||
|
params-needed-path)
|
||||||
|
))
|
||||||
|
|
||||||
;; cell implementation idea taken from prismatic schema library
|
;; cell implementation idea taken from prismatic schema library
|
||||||
(defprotocol PMutableCell
|
(defprotocol PMutableCell
|
||||||
#?(:clj (get_cell [cell]))
|
#?(:clj (get_cell [cell]))
|
||||||
|
|
@ -382,14 +474,14 @@
|
||||||
[^com.rpl.specter.impl.CompiledPath path structure]
|
[^com.rpl.specter.impl.CompiledPath path structure]
|
||||||
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||||
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
||||||
((.-select-executor ex) (.-params path) (.-selector tfns) structure)
|
((.-select-executor ex) (.-params path) (.-params-idx path) (.-selector tfns) structure)
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn compiled-transform*
|
(defn compiled-transform*
|
||||||
[^com.rpl.specter.impl.CompiledPath path transform-fn structure]
|
[^com.rpl.specter.impl.CompiledPath path transform-fn structure]
|
||||||
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||||
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
^com.rpl.specter.impl.ExecutorFunctions ex (.-executors tfns)]
|
||||||
((.-transform-executor ex) (.-params path) (.-transformer tfns) transform-fn structure)
|
((.-transform-executor ex) (.-params path) (.-params-idx path) (.-transformer tfns) transform-fn structure)
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn selected?*
|
(defn selected?*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue