[ANN] Specter 1.1.0: solving the `dissoc-in` problem

215 views
Skip to first unread message

Nathan Marz

unread,
Jan 2, 2018, 9:34:29 AM1/2/18
to Clojure
Specter fills in the holes in Clojure's API for manipulating immutable data, allowing data manipulation to be done concisely and with near-optimal performance. Specter is especially powerful for working with nested and recursive data. 


Excited to be releasing Specter 1.1.0 today. It's not often I discover how to do something new with Specter, but this release includes the `compact` navigator which utilizes a new composition technique. 

If you look at the JIRA for a proposed `dissoc-in` function (https://dev.clojure.org/jira/browse/CLJ-1063), you can see a discussion about whether empty subvalues should be kept or discarded. This is further complicated by the fact that `dissoc-in` must operate on vectors. Ultimately, there is no right answer, as the manipulation needs can differ from usage to usage. You may even want to remove some empty subvalues along the path but not others. The JIRA discussion looks to handle the most common use case instead of handling all use cases.

Specter's paths, on the other hand, can specify much richer behavior. The new `compact` navigator allows you to concisely specify which values along the path should be removed if emptied:

(setval [:a (compact :b :c)] NONE {:a {:b {:c 1}}})
;; => {}

(setval [:a (compact :b :c)] NONE {:a {:b {:c 1} :d 2}})
;; => {:a {:d 2}}

(setval [:a :b (compact :c)] NONE {:a {:b {:c 1}}})
;; => {:a {}}

It works with sequences too:

(setval [1 (compact 0)] NONE [:a [:b] :c])
;; => [:a :c]

And like all navigators, it works recursively as well:

(def TREE-VALUES
  (recursive-path [] p
    (if-path vector?
      [(compact ALL) p]
      STAY
      )))

(setval [TREE-VALUES even?] NONE [1 [2 3] [4] [5 [[6]]]])
;; => [1 [3] [5]]

`compact` is very efficient and has a beautiful implementation: https://github.com/nathanmarz/specter/blob/1.1.0/src/clj/com/rpl/specter.cljc#L1459

The new composition technique is utilizing terminal navigation points inside a reusable navigator. Terminal points were originally conceived for `multi-transform` operations, but to my pleasant surprise turn out to be very useful beyond that.

I don't usually expound on releases like this, but the `compact` navigator, a composition of already existing components, excited me because it shows Specter is more powerful than even I knew. 

The full changelog for the release is here: https://github.com/nathanmarz/specter/blob/master/CHANGES.md
Reply all
Reply to author
Forward
0 new messages