For destructuring a map with namespaced keywords we have a lot of options:
(require '[widget.factory :as factory])
;; direct
(fn [{clip :widget.factory/clip}]
)
;; :keys + symbol
(fn [{:keys [widget.factory/clip]}]
)
;; :keys + keyword
(fn [{:keys [:widget.factory/clip]}]
)
;; same but with "auto-resolved keyword"
(fn [{:keys [::factory/clip]}]
)
;; namespaced ::keys
(fn [{::factory/keys [clip]}]
)
It’s often not clear which one to prefer, especially when destructuring a mix of un-namespaced and namespaced keywords. For example, say you have a map
;; direct
(fn [{:keys [title created body] clip :widget.factory/clip}]
)
;; :keys + symbol
(fn [{:keys [title created body widget.factory/clip]}]
)
;; namespaced ::keys
(fn [{:keys [title created body] ::factory/keys [clip]}]
)
;; namespaced keyword in ::keys
(fn [{:keys [title created body ::factory/clip]}]
)
Discussion welcome. This is just one example, and depending on the exact things you’re destructuring I think people might end up with different choices.
One thing I’ve noticed a lot in the codebase I’m working on is that people will reach for namespaced keywords inside :keys, e.g. {:keys [foo bar :hello.world/baz]}. In this case a symbol would make more sense to me. On the other hand if there’s a namespace alias then using a keyword allows you to abbreviate things, {:keys [foo bar ::w/baz]}, so I think that’s where this tendency to use keywords comes from.
That said, I’d also abbreviate :my.very.long/d (like ::long/d) because abbreviations tend to be pretty consistent. Hopefully. (And searching with a regex like :\S+/d should be manageable.)
I often use the ::keys shorthand to access keywords that “belong to” the current namespace anyways. So more often than not the only difference is :keys and ::keys.
I didn’t even know about the namespaced keyword destructure using ::foo/keys. I can imagine using that for destructuring maps with mixed namespaces in keywords, like
I’d like to chime in and say that I really dislike auto namespaced keywords (:: something). They look nice because you type less, but it means that the namespace of the keyword, which should only express the semantics of the domain, is now tied to the particular file/namespace where you used the keyword. It’s conflating domain organization and files organization which are totally unrelated. Good luck refactoring code when you decided to use this ::syntax, especially if you shared those keywords to the outside world.
That’s why I always use fully qualified keywords hence usually destructure with :keys + symbol. There’s a reason it’s the officially presented way of destructing in the guide, it’s because you should structure semantically, possible using different namespaces. The example cited above by @plexus shows this clearly : in the human map some keys have the namespace “person” while some doesn’t (“hobby”). The semantics matters above all.
Sorry if the tone looks harsh, it’s just that I’m typing on mobile which is really a PITA and I’d like to see this ::syntax disappear from all the code base I use
You make good points. What I advocate around namespace-qualified keywords is:
For internal use only (i.e., solely within the namespace), use ::foo, as if it were a “private” keyword,
For any external use, put those keywords in their own namespace, separate from the code.
That second point extends to clojure.spec use built on those namespace-qualified keywords: the specs belong in that namespace too because they represent domain information.
The only functions I might have in that separate namespace are very lightweight utilities tied very tightly to those keywords (perhaps a few small predicates for specs).