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

+1 vote
in Clojure by

We work on a fairly large clojure codebase and we have encountered this problem quite a lot.

Just a disclaimer, our design has been a "everything is a map", so it has bit us for quite some time and having a solution like below can be debatable

So we have :keys, :strs for destructuring a map

(def my-func [{:keys [a b c] :as z}])

The problem is that, for the caller, it is very hard to know what all the function end up using from the z map (one can always get something more than what's destructured. Worse, my-func ends up passing around z elsewhere and it gets hard to refactor code without going through every place where the z has gone

Instead having the following (naming not important for now)

(def my-func [{:keys-only [a b c] :as z}])

which only extracts a b c from the map and the z contains only a b c keys means the function has a strong contract as to what's required by this function. No more refactoring challenges

4 Answers

+1 vote

You could use only namespaced keys, to add rigor to the codebase search (but you would have to search with proper parsing).

Removal of keys, as with select-keys, smells like a blunt instrument.

If your primary goal is to get control of all matters surrounding data flow, you could consider putting the data-processing under the supervision of something like Prismatic Graph (or was it Plumbing?). What you need is not exactly Graph/Plumbing, but you get the idea and can make your own facility to suit. In return for the bother, you could get not only "enforcement", but also helpful reports that x was unused, or automatic ordering of processing steps to produce custom subsets of the possible outputs.

+1 vote

Would simply removing :as z work? Can't destructure what you don't have. ;)

That certainly can work in few cases :)
0 votes

Have you tried using spec?

I have definitely checked out spec. I was expecting this to be more of a language construct instead of a library. Also, I haven't digged  a  lot into spec so I don't know how the above could be achieved? One way I can think of is

     (def my-func [m] (let [m (s/conform :spec m)]))

Is there a better way?
–2 votes

My rule is to disallow the use of :as.

You can't enforce it (though I guess a linter could). But it's a guideline I go by to avoid specifically the case you are talking about.

It's a little similar too if you did have a :keys-only, you also couldn't really enforce it. That said I personally like the idea, so you get my upvote.

I do think disallowing :as is a simpler solution though. Because there are so many forms of destructuring, that you'd need a "only" variant for all of them.

Maybe instead of having :keys-only, a better approach would be to have a :as-only or something of that sort, maybe it is better to call it :into. Since that would work for all destructuring types.