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

0 votes
ago in Clojure by
edited ago by

With 1.13 Clojure introduces new directives to associative destructuring. Is there a way to get the checked behavior (throw when the key isn't present) on keys destructured without using :keys! like directives ?

I like being able to have vars holding keywords. It allows me to document them and it's also convenient in the case of typos since I tend to catch them sooner in var names rather than in keywords.

For instance consider the follow example :

(def foo
   "My nice doc for foo."
   ::foo)

(def bar
  "My nice doc for bar."
  ::foo)

(def example-map {foo :value})

;; How do I say here that I want foo and bar to be present without a `:keys!` directive ?
(let [{foo-binding foo
            bar-binding bar} example-map]
  [foo-binding bar-binding])

If there isn't a way, would a :checked directive be a good idea :

;; This would throw, `bar` is absent from `example map`
(let [{:checked {foo-binding foo
                   bar-binding bar}} example-map]
  [foo-binding bar-binding])

A :& directive meaning checked and unbound could also be possible:

;; This would throw, `bar` is absent from `example map` but it wouldn't be bound
(let [{:checked {foo-binding foo}
       :& {bar-binding bar}} example-map]
   [foo-binding])

Did I miss that feature somehow? If not what do you think about :checked and
:& directives ?

Cheers,

Edit:

I just thought of another use case for a :checked like directive. Directive-less destructuring allows for binding names different from keyword names. We can do :

(let [{id1 :a/id
       id2 :b/id} some-map]
  ...)

Whereas:

(let [{:keys! [:a/id :b/id]} some-map]
   ...)

does not let you have different bindings for keyword sharing the same name.

Leaving aside the var holding keywords pattern that may be a bad idea. Would the binding of namespace qualified keywords sharing the same name be a valid argument to consider a :checked like directive?

1 Answer

+1 vote
ago by

In general, I think putting keywords in vars like that is an anti-pattern in Clojure. The benefits of keywords diminish when you start adding redirection around them (also, this has a significant performance impact). So, I don't think we will add these features.

ago by
Thanks for the quick answer! I have seen the pattern used elsewhere and liked the documentation aspect of it. So I thought I would ask.

Cheers,
ago by
I agree with Alex that this is an anti-pattern. When we first got started with Clojure at work, back in 2011, we used vars for keywords for the reasons you listed, but over time we found it to be a bit of a maintenance nightmare, because you couldn't visually distinguish symbols-bound-to-keywords from other symbols, so you'd often have to look up the definition to be sure what the code was actually doing, and where we thought we needed documentation was generally a sign that we should have used qualified keywords instead of simple ones. We removed all of those vars over time.

Some judicious use of Spec helped hold our hands as we got rid of the vars, and now we would liberally use :keys! to cover required keys and avoid typos.
ago by
I edited the original post to add another use case for a `checked` like directive that is completely different from the var holding keywords use case.

Thanks for the explanation for this anti-pattern, I'll reconsider in the future.
ago by
You can use both renaming and :keys! [& …] to get this. We did consider various alternatives but this is uncommon enough that it didn’t dominate.
ago by
Interesting, thanks Alex.
...