I find myself doing a lot of
(get-in (swap! nested-map update-in [:a :b] update-fn) [:a :b])
to update a new value and then access it atomically. It seems a bit clunky to me - is there a more elegant way of doing this? reagent
has the cursor
idea which works nicely as once you define the cursor the above just becomes (swap! cursor update-fn)
.
I found https://github.com/rlewczuk/clj-cursor which seems to be what I want but maybe overkill?
Thanks,
Implementing your own cursor based on the clojure.lang.IAtom
interface is fairly straight-forward:
(defn my-cursor [target-atom path]
(reify clojure.lang.IAtom
(swap [_this f] (get-in (swap! target-atom update-in path f) path))))
Note that clojure.lang.IAtom
may be considered an implementation detail of Clojure so whether you want to depend on it is up to you.
Here is an example:
(def nested-map (atom {}))
(defn update-fn [x] (inc (or x 0)))
(def c (my-cursor nested-map [:a :b]))
(swap! c update-fn)
;; => 1
(deref nested-map)
;; => {:a {:b 1}}
Implementing IDeref
may also be useful:
(defn my-cursor [target-atom path]
(reify clojure.lang.IAtom
(swap [_this f] (get-in (swap! target-atom update-in path f) path))
clojure.lang.IDeref
(deref [_this] (get-in (deref target-atom) path))))
Even if you don't have to implement the other methods of the interfaces, it is nevertheless a good idea.
A previous version of this answer used proxy
but reify
is probably better.