specter

Colin P. Hill 2025-06-17T21:43:29.753829Z

Popping in to say my project adopted Specter a few days ago after much hemming and hawing about doing so, and my god our code is so much tidier already. A good chunk of our namespaces are going to be able to cut their size in half or more. Thanks @nathanmarz for the great work! "Clojure's missing piece" is a bold claim, but Specter lives up to it.

🎉 1
Colin P. Hill 2025-06-17T22:07:30.041339Z

There is one transformation I keep finding I have to do, which I haven't found a way to do with Specter yet: moving values from one level of a map hierarchy to another, under a different name, in bulk. Like set/rename-keys, but deep. E.g., start with this structure

[{:a 1, :b 2}
 {:a 3, :b 4}]
and end up with this one
[{:x {:y 1, :z {:w 2}}}
 {:x {:y 3, :z {:w 4}}}]
Does Specter have a way to do this sort of thing that I haven't found yet? EDIT: Tweaked output to illustrate that values may not all land at the same level.

nathanmarz 2025-06-17T22:12:35.581709Z

well, that's a complete transformation of almost the entire thing

nathanmarz 2025-06-17T22:13:03.004319Z

Specter can assist, but the behavior all needs to be specified

nathanmarz 2025-06-17T22:15:22.295479Z

I'm not sure what the rules are supposed to be in this case

nathanmarz 2025-06-17T22:15:26.833139Z

just dispatch on the name of the key?

Colin P. Hill 2025-06-17T22:16:44.256269Z

I guess what I'm reaching for is a way to transform one keypath to another keypath. And then, ideally, do that for multiple keypaths that share a prefix without repeating that prefix over and over.

nathanmarz 2025-06-17T22:19:31.740249Z

this does what you want:

(multi-transform
  [ALL
   (multi-path
     [ALL
      (terminal
        (fn [[k v]]
          (cond (= k :a)
                [:y v]

                (= k :b)
                [:z {:w v}]

                :else
                [k v]
                )))]
     (terminal #(hash-map :x %)))]
  data)

nathanmarz 2025-06-17T22:19:56.018549Z

doesn't seem like you want to specify it like that though

nathanmarz 2025-06-17T22:21:20.754459Z

you want something like (foo {[:a] [:x :y] [:b] [:x :z :w]} data)?

Colin P. Hill 2025-06-17T22:21:29.728859Z

Less contrived, similar to my actual problem shape:

{:edges [{:node {:fooDetail {:fooName "A", :fooDesc "B", :fooBarName "C"}}}
         {:node {:fooDetail {:fooName "D", :fooDesc "E", :fooBarName "F"}}}]}
into
{:edges [{:node {:foo {:name "A", :desc "B", :bar {:name "C"}}}}
         {:node {:foo {:name "D", :desc "E", :bar {:name "F"}}}}]}

Colin P. Hill 2025-06-17T22:21:41.943739Z

Yes, that's basically what is dimly taking shape in my head.

Colin P. Hill 2025-06-17T22:23:23.900239Z

Could straightforwardly write a function to do that for maps-all-the-way-down structures, but I have some cases where integrating with Specter navigators would be handy.

nathanmarz 2025-06-17T22:25:34.278429Z

Specter can help, but major restructurings of data like this aren't going to be one of those examples that are solved by just stringing a couple navigators together

Colin P. Hill 2025-06-17T22:25:57.782729Z

Fair enough! Thanks for the guidance.

Colin P. Hill 2025-06-17T22:27:01.195319Z

It did just occur to me I could write a (foo {[:a] [:x :y] [:b] [:x :z :w]} data) which makes calls to Specter in its implementation, instead of trying to get clever with advanced navigators.

nathanmarz 2025-06-17T22:31:01.033669Z

yup

Colin P. Hill 2025-06-17T22:36:50.235959Z

(sp/transform [:edges sp/ALL :node :fooDetail]
                (fn [node]
                  (remap node {[:fooName]    [:name]
                               [:fooDesc]    [:desc]
                               [:fooBarName] [:bar :name]}))
                input)
{:edges
 [{:node {:fooDetail {:name "A", :desc "B", :bar {:name "C"}}}}
  {:node {:fooDetail {:name "D", :desc "E", :bar {:name "F"}}}}]}
Works like a charm. Thanks again!

Colin P. Hill 2025-06-17T22:37:52.006339Z

(of course that example could implement remap without Specter too, but in principle this is more powerful, and probably also faster)