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

+6 votes
in Clojure by
edited by

Is keys destructuring for documentation reasons good style? The reason I'm asking is that clj-kondo, a linter for Clojure, reports unused bindings. E.g.:

(let [x 1, y 2] y)

results in a warning about x being unused.

Sometimes people do this:

(defn public-foo [{:keys [foo bar] :as x}]
  (private-baz x))

only to get a better docstring (or generated documentation) for public-foo. But they will get a warning about foo and bar being unused. An example from the wild: link.

Destructuring is not free in terms of performance, so using spec or :arglists may be a better alternative to get these documentation benefits. E.g.:

user=>
(defn public-foo
  {:arglists '([{:keys [foo bar] :as x}])}
  [x]
  ;; (private-baz x)
  )
#'user/public-foo
user=> (doc public-foo)
-------------------------
user/public-foo
([{:keys [foo bar], :as x}])
nil

I'd like some consensus on this before I make any configuration support in clj-kondo to suppress warnings for unused bindings introduced by keys destructuring in function arguments.

6 Answers

+3 votes
by

I never destructure :keys just for documentation; the alternatives you mentioned both sound better. I do often add the :as part without using it, but i'm sure there is no perf penalty in that case.

And now i have my own question...why aren't you using symbols, like {:keys [foo bar]}? I'm guessing there's no difference, but sometimes little things i should've learned years ago reveal themselves like a perry mason moment, so i gotta ask.

by
The one reason we made keywords work there was to get auto resolved keyword support (::), but that’s actually been mostly superseded by having :foo/keys and ::keys now. So just use symbols.
by
I picked up that habit when working on a codebase where this style was prevalent, not for any other reason.
by
Ah, I've been using `{:keys [:some/namespaced-keyword :some/other-keyword]}` quite a bit, mainly so that it's easier to find usages - i.e. grepping for `:some/namespaced-keyword`will find the full version, but not `{:some/keys [namespaced-keyword]}`. Is this frowned upon?
by
No, totally fine.
0 votes
by

My style in that situation would normally be:

(defn public-foo [{:keys [foo bar] :as x}]
  (->> (private-baz foo bar)
    (assoc x :res)))
0 votes
by

I never destructure for the sake of arglists in docs.

Using keywords instead of symbols in :keys is gross.

0 votes
by

I often destructure in fn declarations to clarify usage for myself. I understand that this practice makes linting more difficult but I'd say it is something that people do and a knob to turn off linting notifications for this would be a nice to have.

0 votes
by

I'll plead guilty, but I don't think that's good practice

0 votes
by

I do this, I like it, but it's an opinion and there isn't a right or wrong here.

Good style is largely in the eye of the beholder. Beware of anyone that asserts strong opinions about things this trivial.

...