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}}]
|
||||
(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
|
||||
|
||||
(def ALL (i/->AllStructurePath))
|
||||
|
|
@ -118,8 +167,10 @@
|
|||
|
||||
(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))
|
||||
|
||||
;;TODO: should be parameterized
|
||||
(defn srange [start end] (srange-dynamic (fn [_] start) (fn [_] end)))
|
||||
|
||||
(def BEGINNING (srange 0 0))
|
||||
|
|
@ -130,9 +181,33 @@
|
|||
|
||||
(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 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))
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@
|
|||
[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
|
||||
(comp-paths* [paths]))
|
||||
|
||||
|
|
@ -38,13 +45,13 @@
|
|||
(def RichPathExecutor
|
||||
(->ExecutorFunctions
|
||||
:richpath
|
||||
(fn [params selector structure]
|
||||
(selector params 0 [] structure
|
||||
(fn [params params-idx vals structure]
|
||||
(fn [params params-idx selector structure]
|
||||
(selector params params-idx [] structure
|
||||
(fn [_ _ vals structure]
|
||||
(if-not (empty? vals) [(conj vals structure)] [structure]))))
|
||||
(fn [params transformer transform-fn structure]
|
||||
(transformer params 0 [] structure
|
||||
(fn [params params-idx vals structure]
|
||||
(fn [params params-idx transformer transform-fn structure]
|
||||
(transformer params params-idx [] structure
|
||||
(fn [_ _ vals structure]
|
||||
(if (empty? vals)
|
||||
(transform-fn structure)
|
||||
(apply transform-fn (conj vals structure))))))
|
||||
|
|
@ -53,19 +60,30 @@
|
|||
(def StructurePathExecutor
|
||||
(->ExecutorFunctions
|
||||
:spath
|
||||
(fn [params selector structure]
|
||||
(fn [params params-idx selector structure]
|
||||
(selector structure (fn [structure] [structure])))
|
||||
(fn [params transformer transform-fn structure]
|
||||
(fn [params params-idx transformer transform-fn structure]
|
||||
(transformer structure transform-fn))
|
||||
))
|
||||
|
||||
(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
|
||||
;; (just calls bind-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]
|
||||
(->> aseq
|
||||
(filter (partial = val))
|
||||
|
|
@ -123,9 +141,8 @@
|
|||
afn (fn [params params-idx vals structure next-fn]
|
||||
(next-fn params params-idx (conj vals (cfn this structure)) structure)
|
||||
)]
|
||||
(->CompiledPath
|
||||
(no-params-compiled-path
|
||||
(->TransformFunctions RichPathExecutor afn afn)
|
||||
nil
|
||||
)))
|
||||
|
||||
|
||||
|
|
@ -133,28 +150,26 @@
|
|||
(let [pimpl (structure-path-impl this)
|
||||
selector (:select* pimpl)
|
||||
transformer (:transform* pimpl)]
|
||||
(->CompiledPath
|
||||
(no-params-compiled-path
|
||||
(->TransformFunctions
|
||||
StructurePathExecutor
|
||||
(fn [structure next-fn]
|
||||
(selector this structure next-fn))
|
||||
(fn [structure next-fn]
|
||||
(transformer this structure next-fn)))
|
||||
nil
|
||||
)))
|
||||
|
||||
(defn coerce-structure-path-rich [this]
|
||||
(let [pimpl (structure-path-impl this)
|
||||
selector (:select* pimpl)
|
||||
transformer (:transform* pimpl)]
|
||||
(->CompiledPath
|
||||
(no-params-compiled-path
|
||||
(->TransformFunctions
|
||||
RichPathExecutor
|
||||
(fn [params params-idx vals structure next-fn]
|
||||
(selector this structure (fn [structure] (next-fn params params-idx vals structure))))
|
||||
(fn [params params-idx vals structure next-fn]
|
||||
(transformer this structure (fn [structure] (next-fn params params-idx vals structure)))))
|
||||
nil
|
||||
)))
|
||||
|
||||
(defn structure-path? [obj]
|
||||
|
|
@ -264,8 +279,10 @@
|
|||
(transformer params 0 vals structure
|
||||
(fn [_ _ vals-next structure-next]
|
||||
(next-fn x-params params-idx vals-next structure-next)
|
||||
)))
|
||||
))))))
|
||||
))))
|
||||
params
|
||||
0
|
||||
)))))
|
||||
|
||||
(extend-protocol PathComposer
|
||||
nil
|
||||
|
|
@ -294,7 +311,7 @@
|
|||
)
|
||||
needs-params-paths (filter #(instance? ParamsNeededPath %) coerced)]
|
||||
(if (empty? needs-params-paths)
|
||||
(->CompiledPath result-tfn nil)
|
||||
(no-params-compiled-path result-tfn)
|
||||
(->ParamsNeededPath
|
||||
(coerce-tfns-rich result-tfn)
|
||||
(->> 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
|
||||
(defprotocol PMutableCell
|
||||
#?(:clj (get_cell [cell]))
|
||||
|
|
@ -382,14 +474,14 @@
|
|||
[^com.rpl.specter.impl.CompiledPath path structure]
|
||||
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||
^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*
|
||||
[^com.rpl.specter.impl.CompiledPath path transform-fn structure]
|
||||
(let [^com.rpl.specter.impl.TransformFunctions tfns (.-transform-fns path)
|
||||
^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?*
|
||||
|
|
|
|||
Loading…
Reference in a new issue