clojure 2026-07-23

With the new :select and :defaults, was any consideration given to a “merge input map with defaults” value (like the :as+defaults in the example in 🧵)? It seems to me this would be useful when passing values on to other functions.

Example:

(deftest clj-1-13-destructuring-test
  (let [input {:a 1 :b 2 :z 26}]
    (is (= {:a           1
            :b           2
            :c           103
            :select      {:a 1 :b 2 :c 103}
            :as          {:a 1 :b 2 :z 26}
            :as+defaults {:a 1 :b 2 :c 103 :z 26}}
           (let [{:as       as
                  :keys     [a b c]
                  :or       {c 103}
                  :defaults defaults
                  :select   select} input]
             {:a           a
              :b           b
              :c           c
              :select      select
              :as          as
              :as+defaults (merge defaults as)})))))

➕ 1

isn't that the point of defaults, that you can merge it at will?

or did you want another key in there with :as+defaults m (with better name) that does this for you?

Alex Miller (Clojure team) 2026-07-23T12:23:28.675399Z

Yes, this was considered. :defaults and :as gives you the tools to do this in the body if needed

👍 1

> or did you want another key in there with :as+defaults m (with better name) that does this for you? Yes, that’s what I was wondering about.

with the caveat that defaults + merge only handle one level

👍 1

I think if it went down that route, you'd have a slipery slope of how many "convenience merges" map you want to add to destructuring. What would be nice though, is if there was a way to handle the multi-level merge more easily, because currently you have to basically declare many level deep of defaults to do so.

➕ 1

A bit of a contrived example, but now you have to do something like:

user=> (let [{:as m
  #_=>        :keys [a]
  #_=>        :or {:a 1}
  #_=>        :defaults d1
  #_=>        {:keys [c]
  #_=>         :or {:c 2}
  #_=>         :defaults d2
  #_=>         {:keys [e]
  #_=>          :or {:e 3}
  #_=>          :defaults d3
  #_=>          {:keys [g]
  #_=>           :or {:g 4}
  #_=>           :defaults d4}
  #_=>          :f}
  #_=>         :d}
  #_=>        :b}
  #_=>       {:b {:d {:f {}}}}]
  #_=>   (-> (merge d1 m)
  #_=>       (update :b #(merge d2 %))
  #_=>       (update-in [:b :d] #(merge d3 %))
  #_=>       (update-in [:b :d :f] #(merge d4 %))))
{:a 1, :b {:c 2, :d {:e 3, :f {:g 4}}}}

I guess it’s easier to define defaults separately and do a deep merge.

(deftest diy-multi-level-defaults-test
  (let [destructure (fn [m]
                      (let [defaults {:a 1
                                      :b {:b-a 21
                                          :b-b 22}}
                            mm (deep-merge defaults m)
                            {a :a
                             {:keys [b-a b-b]} :b} mm]
                        [a b-a b-b]))]
    (is (= [1 21 22]
           (destructure {})))
    (is (= [101 121 22]
           (destructure {:a 101
                         :b {:b-a 121}})))))

@nomiskatz Ya but :defaults rationale says it was added specifically so you don't have to repeat the defaults both in the :or and inside the body.

@didibus (I suspect I’m not understanding you, but…) I’m not using :or and defaults is used in only one place — there’s no duplication.

Alex Miller (Clojure team) 2026-07-24T18:28:37.260089Z

might want to check out the latest commits in master for a new :all directive

👀 2
🔥 1

@nomiskatz right, I mean if you were to use :or already, but now also want to default :as. And also I'm assuming the prime use-case would be to use it inside function definition where you won't be able to apply this preprocessing to the input map.

This is moot now that we’re going to have :all, but… > I mean if you were to use :or already, but now also want to default :as. Right. > I’m assuming the prime use-case would be to use it inside function definition where you won’t be able to apply this preprocessing to the input map. Still not sure I’m understanding. Can’t you rewrite it as (defn foo [m] (let [defaults ..., mm (deep-merge defaults m] ...)) ?

Yes you can, it's just not as nice as having the destructuring directly in the fn binding form 😛. That's what I meant.

👍 1

> might want to check out the latest commits in master for a new :all directive Roll on Alpha 6! 🙂

I guess I need to make yet another change in clj-kondo soon? ;)

Alex Miller (Clojure team) 2026-07-24T19:37:18.114179Z

not going to do a release today, probably early next week

you don't do friday evening releases? ;)

With more secret stuff added? 😄

:all - what more do you want?

🎶 "We want it :all, and we want it now!" 🎵

✔️ 3

I saw Rich had committed checks on unbound/unreferenced keys in :or as well—a useful sanity check! Thank you!

yes, it will be checked only on new paths (use of :defaults :select :all) initially, since we think there may be extant cruft in codebases

👍🏻 1

I prefer to think of them as under-promised rather than secret

🤔 1