specter

Latest version: 1.1.3
justinlee 2018-02-04T00:20:11.000020Z

I use specter to manipulate my state object in the front end. I’m curious: is there a way to apply multiple specter transformations to an atom atomically? If I were doing this with the normal swap! function I’d just wrap all the transformations in a fn. But I’m not sure how to do that with specter. (I’m asking because I’m worried that applying multiple specter transformations to a reagent atom might cause me grief, given that components get re-rendered in response to updates. So far, I’m not 100% this is a real problem, because I think reagent batches updates, but I’m still curious.)

nathanmarz 2018-02-04T00:28:55.000059Z

@lee.justin.m yes you can use multi-transform for that

nathanmarz 2018-02-04T00:29:30.000044Z

e.g. (multi-transform [ATOM (multi-path [:a (terminal inc)] [:b (terminal dec)])] state-object)

justinlee 2018-02-04T00:29:46.000068Z

@nathanmarz super thanks!

justinlee 2018-02-04T00:31:11.000084Z

oh i hadn’t seen this list-of-macros page. there are all kinds of goodies here.

nathanmarz 2018-02-04T00:35:48.000053Z

definitely worth skimming through the wiki

nathanmarz 2018-02-04T00:35:52.000088Z

it's mostly up to date nowadays

justinlee 2018-02-04T20:23:23.000151Z

@nathanmarz Just following up on the example you just used in #clojure, could you explain this:

(def data 
  {:a [:x :y]
   :b [:x :y]})
(select [ALL (collect-one FIRST) LAST] data)
;; -> [[:a [:x :y]] [:b [:x :y]]]

;; The next statement will distribute the :a and :b
(select [ALL (collect-one FIRST) LAST ALL] data)
;; -≥ [[:a :x] [:a :y] [:b :x] [:b :y]]

;; But this, which applies the same selector to the intermediate result, does not
(select [ALL] [[:a [:x :y]] [:b [:x :y]]])
;; -> [[:a [:x :y]] [:b [:x :y]]]

schmee 2018-02-04T20:29:20.000063Z

@justinlee collecting variables is a special thing, search for “collect” in the readme for an explanation: https://github.com/nathanmarz/specter

justinlee 2018-02-04T20:31:58.000114Z

@schmee thanks yea i read that and the docs for collect and collect-one but i can’t for the life of me figure out how they alter the ALL selector in the context of a select

justinlee 2018-02-04T20:32:55.000114Z

there is some reference to adding a value to the ‘collected values’ but I haven’t seen how the collected values are used in selectors like ALL (though I understand how they are used in the transform example when you need an argument to the transforming function

schmee 2018-02-04T20:34:30.000093Z

user=> (select [ALL (collect-one FIRST) LAST] data)
[[:a [:x :y]] [:b [:x :y]]]
user=> (select [ALL (collect-one FIRST) (collect-one FIRST) LAST] data)
[[:a :a [:x :y]] [:b :b [:x :y]]]

schmee 2018-02-04T20:34:55.000039Z

looks to me like select just calls vector on the collected arguments + the value(s) you’ve navigated to

justinlee 2018-02-04T20:39:23.000052Z

Yea I guess that’s it. I’m not sure why it does that. 🙂

com.rpl.specter=> (select [(putval "a") ALL] [1 2 3])
[["a" 1] ["a" 2] ["a" 3]]
com.rpl.specter=> (select [(putval "a") FIRST] [1 2 3])
[["a" 1]]
com.rpl.specter=> (select [(putval "a") (putval "b") ALL] [1 2 3])
[["a" "b" 1] ["a" "b" 2] ["a" "b" 3]]

nathanmarz 2018-02-04T21:14:09.000073Z

@lee.justin.m this is the code that creates that behavior: https://github.com/nathanmarz/specter/blob/master/src/clj/com/rpl/specter/impl.cljc#L294

justinlee 2018-02-04T21:15:03.000038Z

got it thanks. that makes sense.