plumatic / plumbing

Prismatic's Clojure(Script) utility belt

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`map-nested` or something

gfredericks opened this issue · comments

For years and years and years and years I keep running into the issue of how awkward it is to mix threading macros with updating the values in a collection.

I'm not sure if this can be made easier without getting into crazy-threading-macro-complexity-hell or something like that, but I thought I would check if you folks have some alternate approach, or if we might discover a decent extension for plumbing.

To be concrete, I often find myself doing something like:

(defn the-business-logic
  [m]
  (-> m
      (update :logic-count inc)
      (update :clients (fn [clients] (map (fn [client]
                                            (-> client
                                                (update-in ...)))
                                          clients)))))

This could also be accomplished with (partial map (fn [client] ...)) or even nicer with the fn-> I just re-discovered a few minutes ago, but it still feels like it takes a lot of work to map a nested collection.

Approaches using functions that don't exist yet:

  • The super higher-order (defn flip [f] (fn [x y] (f y x))) could be used to say (update :clients (flip map) (fn-> ...))
  • I once defined a map-nested that let me do (map-nested :clients (fn-> ...))

I dunno!

One nice thing about flip is that it would also work with other sorts of wrong-order functions like map-vals, map-keys, etc.

@gfredericks If you're not set on composing these transformations yourself, libraries like spectre and instar both provide power-update style functions which make exactly those sorts of examples simple and declarative.

Understand your gist here is chasing simple, composable pieces, but decided to crash this party anyway 👪

Thanks for the pointers; spectre is definitely too much macro for me, and I think you're right that I'd prefer the composable pieces to a more comprehensive approach.

I can grant that comprehensive approaches might do everything I want, but the social cost of adopting a large thing is pretty high.

No arguments there!

A map + fn-> combination to me than update + map. Obliviates the need for map-nested-in.

(defn the-business-logic [m]
  (-> m
      (update :logic-count inc)
      (update :clients (each-> (update-in ...)))))

The each-> function could preserve the collection type too I guess.

Our usual idiom is e.g.

(update x :clients (fn->> (map #(update % :foo inc))))

I guess with something like flip you can drop a level of nesting but it doesn't really seem simpler, and IMO is potentially harder to understand. Similarly I don't think we want to get into all permutations of map/filter/remove and ->/->>. That said, we're open to ideas :).

Sounds good to me, thanks