clojure-dev 2026-10-24

Another thing, that for me is not very intuitive in the new desctructuring syntax is the :select directive.

(def small-map {:a 5})
(let [{:keys [a b & :c], :as m, :or {a 42 b 3 :c "unknown"}, :select sel, :defaults df} small-map]
  (println sel))
sel is the binding, which is the result of the selection. But :select sel suggests that sel is the query. Sorry, I don't want to criticise this. I don't want to influence the language. I am happy with everything. But this new destructuring syntax is the first time I read something, which for me looks a bit inconsistent. And as it is an alpha release, I just want to share this vague feeling.

Two other things, I just realised: • & in :keys for me is another inconsistency. In the example :a and :b are optional and will be bound while :c is required, but will not be bound. • When reading https://clojure.org/guides/destructuring it is not described, what :defaults df is. Is it the :or defaults?

I don't think :c is going to be required in your example. :select might just be a shortened "selection" btw?

:defaults was removed

:select is just new to you. Rich talks about how destructuring is making a selection of incoming keys (and their values) so :select is a very appropriate name. You could argue :selection might be better but why make everyone type an extra three letters when they "select" a subset of the incoming map? Also, consider this functionality in the context of the other new directives: :missing, :excess, and :all (and now :req! to handle required keys bound to other symbols).

The destructuring guide does describe :defaults but that has been removed and the guide has not yet been updated: > To capture the default values stated in the :or map for some purpose later in your code without restating them, you can use the :defaults directive [since 1.13], which binds a name to a map, created during destructuring, of keys to their default values.

(with the other four directives all merging the :or defaults into their results, :defaults is not useful)

It's worth mentioning that all of these new features have undergone heavy revision over the last few months, so the documentation and examples are still catching up.

👍🏻 1

I strongly recommend folks try these new features out in real world code—not just one-line, toy examples—and learn how they help solve real world problems. Some things don't seem to make sense until you've used them on bigger problems. I've posted several snippets from our codebase at work, across multiple threads here, showing how we are using these new features in production code, and almost every time someone has commented how much my real world examples have helped them understand the new features better. And, yes, we have Alpha 10 in production, using nearly all these new features (including :req! added in Alpha 10). Only on one server so far, but full rollout will happen on Monday, I expect.

Yes, I saw :default in the description, but did not understand it. Ok, not relevant any more. Why typing an extra letter? Because "select" is a verb and "selection" a noun. But "selection" is also not clear. "Selection" means the process of selecting something and also the result if this process.

Naming is hard 😉

About get-in inlining, in CLJD I have been shy of inlining it as a naive sequence of gets because it subtly changes evaluation order and side-effecting code may notice/break (that’s why CLD conservatively inlines get-in as a let with all expressions to evaluate in order before getting to the gets) Do we care?

Why would the order be different?

user> (get-in {1 {2 :hi}} [(doto 1 println) (doto 2 println)])
1
2
:hi
user> (-> {1 {2 :hi}} (get (doto 1 println)) (get (doto 2 println)))
1
2
:hi

You’re right I got doubly confused: 1/ because before doing get-in I inlined some* var arg assoc to a conj between maps and thus order may becomes hash order. 2/ I used some-> instead of -> because once you go nil... and thus I could short-circuit some evaluations.

Looking at the new destructuring features as a whole in 1.13, and Rich's "In Context" talk, it seems we now have a good story for using qualified keys in maps, where we can use Spec to define specific keys' possible values globally, and destructuring to define the specific sets of keys (and their nested structure) at the various points of use. And we can avoid all those s/nilable Specs! We can then use (s/valid? (s/keys) select-map) (or s/conform or s/explain*) to validate the contents of arbitrary maps of qualified keys, without defining specific aggregates via s/keys. This seems very nice (and maybe an extra arity on those Spec functions would allow us to omit (s/keys) and have that be the default assumption of a Spec?). This approach would definitely encourage me to lean on qualified keys more, and to ensure that those qualified names represented genuinely unique semantics in the domain I'm modeling. Which all leads me to ask: is there something being considered for Spec-like validation of (arbitrary) maps with unqualified keys? (and would that be in the 1.13 timeframe or later?).

🆒 5
Alex Miller (Clojure team) 2026-10-24T23:20:22.260229Z

In regards to your last questions, yes and later

2

Awesome. Thank you.

selector seems like a pretty nice way to name a shape and reuse that in multiple places but, unless I'm missing something, the downside is that if the destructuring isn't inline, then there's no documentation in the arg list about what the function requires of its caller. Is there a way (maybe future work?) to somehow declare that a particular selector function applies to an argument?

Seems like it would be a good question for https://ask.clojure.org

Adding the inbound map to :arglists metadata on the returned fn could provide that, yes?

How do you mean? :arglists on selector? I guess if the structure were preserved there, then it would be possible to pull that out and apply it to an arg of my function f? I don't have an ask account yet. I'll try and get something up there when I've got a minute if you think it'd be useful

to do this yourself, you could write something like this (on my phone, apologies)

(def my-selector
  {:arglists '([{:select ...}])}
  (selector {:select ...}))

The arg list of the returned function, right @jason.bullers?

But then I'm repeating right? To be clearer, let's say I have a particular shape, call it "contact with email", and I'd like to reuse that in a few places (send registration, send marketing, send password reset, etc). I could lift the shape:

(def contact-with-email
  (selector {,,,}))
But I want that shape documented in
(defn send-marketing [contact]
  ,,,)

If I did the destructuring inline in send-marketing, it's part of the doc. I'd like that to happen here too, even though I lifted the destructuring up into a selector

What is contact in that defn?

Whatever stuff, but I want to say specifically a required email key, maybe nested

That's what the new destructuring gets me right? I can declare the shape in the arg list

And you call contact-with-email inside it, yes?

Okay then, I do not understand your original question. I thought I did, but now you've confused me.

But how does someone looking at the doc/signature of send-marketing know what needs to be provided?

selector isn't a replacement for argument destructuring...

I'll try to write up something clearer

But if I have the same destructuring, shouldn't I lift that and name it?

Why repeat it in multiple places? That's error prone

That's the point of a spec right?

Name it once, use it where needed

selector produces no bindings so it's not the same semantic you are asking for here...

Do I need bindings though? I have the right shape. I can just pull things out in a let

But the shape is reified

Need to step away for a bit

Okay, I think selector is a red herring here. Consider:

(defn send-marketing [contact]
  (if-let [valid-contact (some-validation-of-contact-fn contact)]
    (let [email (:email valid-contact)]
      ...)))
How would expect that arbitrary validation call to somehow be attached to the signature of send-marketing?

Yeah, of course. I'm just wondering if there's a story for selector and the destructuring in the same way that specs can be attached to a function to enrich their documentation

If we have this nice way to communicate structure and a way to lift that structure into a selector function, I don't think it's unreasonable to wonder if there's a way to put them together 🙂