Welcome! Please see the About page for a little more info on how this works.

+2 votes
in Collections by
retagged by

The & args in update, swap! and etc are very ergonomic and compose really well, so I was surprised to learn that update-keys and update-vals don't have that, they're only [m f].

What is the reason for this?

I think adding & args to these functions would be more consistent with the existing core functions and help avoid anon functions where not needed.

1 Answer

+1 vote

There are a number of functions similar to update-keys and update-vals in existing utility libraries (usually called map-keys / map-vals). afaik, those functions have not had the update extra arg semantics and that's not been something that people have asked for.

One difference is that update etc invoke the f on a single value, whereas update-keys and update-vals invoke f on many values. I'm not sure how often you would want to pass the same static trailing args to the function being applied to every key or value. I don't have any examples of that (but maybe that's just because the existing functions don't do this right now).

A workaround is of course to push that into the f via partial or anonymous function, so maybe that's something that could be searched for.

Do you have a specific use case where you needed this?

You can find some examples with:

* https://grep.app/search?q=map-keys%20%23%28&filter[lang][0]=Clojure
* https://grep.app/search?q=map-vals%20%23%28&filter[lang][0]=Clojure

Not all of these fit the pattern that would be needed but certainly some do.
edited by
Since the util libraries mostly treat these as if they were sequence functions the arity doesn't work for adding the `& args`, which I think is one reason.

My specific use-case was when cleaning up some records from a nested structure in an atom when I was done with the records, but wanted to keep the rest of the data around. Something like:

(def *state (atom {"table" {:schema (,,,) :records [,,,]}}))

;; I didn't expect this to blow up.
(swap! *state update-vals assoc :records [])

;; This works, but is arguably less elegant.
(swap! *state update-vals #(assoc % :records []))

I found these examples in the wild where this could have been useful:

* https://github.com/penpot/penpot/blob/develop/frontend/src/app/worker/import.cljs#L351
* https://github.com/penpot/penpot/blob/develop/backend/src/app/rpc/queries/files.clj#L273
* https://github.com/clojure/tools.analyzer/blob/master/src/main/clojure/clojure/tools/analyzer/passes/uniquify.clj#L31
* https://github.com/exoscale/deps-modules/blob/master/src/exoscale/deps_modules.clj#L154
* https://github.com/exoscale/deps-modules/blob/master/src/exoscale/deps_modules.clj#L156

It's probably more useful in practice for `update-vals` than `update-keys`, but for a contrived example you may want to postfix something to all the keys in a map.

(update-keys {"table" {}} str "__v1") => {"table_v1" {}}