From 858b0b488dde995bdeeb807e091391f68f48385e Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Wed, 22 Jun 2016 14:20:50 -0400 Subject: [PATCH 1/2] implemented multi-transform and terminal --- src/clj/com/rpl/specter.cljx | 40 ++++++++++++++++++++++++------ src/clj/com/rpl/specter/impl.cljx | 15 +++++++++++ src/clj/com/rpl/specter/macros.clj | 11 ++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index bed3a97..f9865f7 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -60,7 +60,7 @@ (compiled-select (i/comp-paths* path) structure)) -(def ^{:doc "Version of select-one that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of select-one that takes in a path precompiled with comp-paths"} compiled-select-one i/compiled-select-one*) (defn select-one* @@ -68,7 +68,7 @@ [path structure] (compiled-select-one (i/comp-paths* path) structure)) -(def ^{:doc "Version of select-one! that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of select-one! that takes in a path precompiled with comp-paths"} compiled-select-one! i/compiled-select-one!*) (defn select-one!* @@ -76,7 +76,7 @@ [path structure] (compiled-select-one! (i/comp-paths* path) structure)) -(def ^{:doc "Version of select-first that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of select-first that takes in a path precompiled with comp-paths"} compiled-select-first i/compiled-select-first*) @@ -85,7 +85,7 @@ [path structure] (compiled-select-first (i/comp-paths* path) structure)) -(def ^{:doc "Version of select-any that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of select-any that takes in a path precompiled with comp-paths"} compiled-select-any i/compiled-select-any*) (def ^{:doc "Global value used to indicate no elements selected during @@ -98,7 +98,7 @@ [path structure] (compiled-select-any (i/comp-paths* path) structure)) -(def ^{:doc "Version of selected-any? that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of selected-any? that takes in a path precompiled with comp-paths"} compiled-selected-any? i/compiled-selected-any?*) (defn selected-any?* @@ -108,7 +108,7 @@ ;; Reducible traverse functions -(def ^{:doc "Version of traverse that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of traverse that takes in a path precompiled with comp-paths"} compiled-traverse i/do-compiled-traverse) (defn traverse* @@ -119,7 +119,7 @@ ;; Transformation functions -(def ^{:doc "Version of transform that takes in a path pre-compiled with comp-paths"} +(def ^{:doc "Version of transform that takes in a path precompiled with comp-paths"} compiled-transform i/compiled-transform*) (defn transform* @@ -128,6 +128,18 @@ [path transform-fn structure] (compiled-transform (i/comp-paths* path) transform-fn structure)) +(def ^{:doc "Version of `multi-transform` that takes in a path precompiled with `comp-paths`"} + compiled-multi-transform i/compiled-multi-transform*) + + +(defn multi-transform* + "Just like `transform` but expects transform functions to be specified + inline in the path using `terminal`. Error is thrown if navigation finishes + at a non-`terminal` navigator." + [path structure] + (compiled-multi-transform (i/comp-paths* path) structure)) + + (def ^{:doc "Version of setval that takes in a path precompiled with comp-paths"} compiled-setval i/compiled-setval*) @@ -193,6 +205,20 @@ (transform* [this structure next-fn] (next-fn structure))) + +(def + ^{:doc "For usage with `multi-transform`, defines an endpoint in the navigation + that will have the parameterized transform function run. The transform + function works just like it does in `transform`, with collected values + given as the first arguments"} + terminal + (richnav 1 + (select* [params params-idx vals structure next-fn] + (i/throw-illegal "'terminal' should only be used in multi-transform")) + (transform* [params params-idx vals structure next-fn] + (i/terminal* params params-idx vals structure) + ))) + (def ^{:doc "Navigate to every element of the collection. For maps navigates to a vector of `[key value]`."} diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index b27c8a1..d5f2e1a 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -1103,6 +1103,13 @@ next-fn ))) +(defn terminal* [params params-idx vals structure] + (let [afn (aget ^objects params params-idx)] + (if (identical? vals []) + (afn structure) + (apply afn (conj vals structure))) + )) + (defn filter-select [afn structure next-fn] (if (afn structure) (next-fn structure) @@ -1481,6 +1488,14 @@ (get-cell state)] )) +(defn- multi-transform-error-fn [& nav] + (throw-illegal + "All navigation in multi-transform must end in 'terminal' " + "navigators. Instead navigated to: " nav)) + +(defn compiled-multi-transform* [path structure] + (compiled-transform* path multi-transform-error-fn structure)) + #+clj (defn extend-protocolpath* [protpath protpath-prot extensions] (let [extensions (partition 2 extensions) diff --git a/src/clj/com/rpl/specter/macros.clj b/src/clj/com/rpl/specter/macros.clj index 65d7bdd..ad1b028 100644 --- a/src/clj/com/rpl/specter/macros.clj +++ b/src/clj/com/rpl/specter/macros.clj @@ -643,6 +643,17 @@ [apath transform-fn structure] `(i/compiled-transform* (path ~apath) ~transform-fn ~structure)) +(defmacro multi-transform + "Just like `transform` but expects transform functions to be specified + inline in the path using `terminal`. Error is thrown if navigation finishes + at a non-`terminal` navigator. + This macro will attempt to do inline factoring and caching of the path, falling + back to compiling the path on every invocation if it's not possible to + factor/cache the path." + [apath structure] + `(i/compiled-multi-transform* (path ~apath) ~structure)) + + (defmacro setval "Navigates to each value specified by the path and replaces it by `aval`. This macro will attempt to do inline factoring and caching of the path, falling From 50d2aa48f57df75b8395950ac0511f63849da400 Mon Sep 17 00:00:00 2001 From: Nathan Marz Date: Wed, 22 Jun 2016 21:00:31 -0400 Subject: [PATCH 2/2] add terminal-val --- src/clj/com/rpl/specter.cljx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/clj/com/rpl/specter.cljx b/src/clj/com/rpl/specter.cljx index f9865f7..0175491 100644 --- a/src/clj/com/rpl/specter.cljx +++ b/src/clj/com/rpl/specter.cljx @@ -9,6 +9,7 @@ defnav defpathedfn richnav + defnavconstructor ]] [com.rpl.specter.util-macros :refer [doseqres]] @@ -21,7 +22,8 @@ defcollector defnav defpathedfn - richnav]] + richnav + defnavconstructor]] #+clj [com.rpl.specter.util-macros :only [doseqres]] ) (:require [com.rpl.specter.impl :as i] @@ -219,6 +221,13 @@ (i/terminal* params params-idx vals structure) ))) +(defnavconstructor terminal-val + "Like `terminal` but specifies a val to set at the location regardless of + the collected values or the value at the location." + [p terminal] + [v] + (p (constantly v))) + (def ^{:doc "Navigate to every element of the collection. For maps navigates to a vector of `[key value]`."}