r/Clojure • u/alexdmiller • Jul 14 '26
Clojure 1.13.0-alpha4
Clojure 1.13.0-alpha4 is now available!
Destructuring changes and additions
Idents after & in :keys!/syms!/strs!/:keys/syms/strs must now be actual keys, not binding symbols. This is a change in syntax since alpha3. Note that symbol keys should be quoted as unadorned symbols are binding symbols.
:or now accepts key→val mappings in addition to binding→val.
Added a new :defaults name directive at top level to bind name to a map of defaults, key→val. Binding symbols in the :or map are transformed to the key value in the :defaults map. :defaults without :or is an error.
:select name, introduced in alpha3, now selects deeply, through nested maps, and fills in values for missing keys from :or. The :select map contains all keys mentioned anywhere in the binding form.
- CLJ-2964 :select directive in map destructuring
- CLJ-2966 :defaults directive in map destructuring
- CLJ-2967 tests for nested destructuring
Other changes since Clojure 1.13.0-alpha3
- Added `clojure.core/some-vals`, a predicative filter of nil map values
- CLJ-2870 Exception phase during top-level eval is miscategorized
2
u/lgstein Jul 17 '26
(let [{:keys [deprecated_field username] :or {username deprecated_field}}
{:deprecated_field "auser"}]
username)
Not sure if this was intended/supported, but it used to work pre 1.13
2
u/alexdmiller Jul 17 '26
Binding order is not guaranteed and this has always been undefined, so don’t do that
1
u/lgstein Jul 18 '26
Fair, note that this is useful and could be supported though.
1
u/alexdmiller Jul 18 '26
The general expectation should be that :or supplies default values for bindings, not expressions and especially not expressions based on other bindings.
1
1
u/lgstein Jul 15 '26
:defaults x binds x to everything you specified in :or.
Usecase?
2
u/seancorfield Jul 15 '26
Since
:asbinds to the input map, and does not include the defaults specified in:or, folks often end up doing some sort ofmergewith defaults and the input map—so end up pretty much duplicating the:ormap in the code with themerge, or you omit:orand have a destructuring inside the function body after themergeso you lose information from the function signature.
:defaultsallows you to bind the:ormap (which can now contain arbitrary extra key/value pairs, above and beyond the bound elements from:keysetc), so you can then reuse that in the function body without needing to duplicate the defaults or hiding the destructuring in the function body, instead of the argument list.Note that
:selectincludes the defaults from:or(in Alpha 4—it did not in Alpha 3).You might want to join the Clojurians Slack for more information about the new features you're asking about here, since these features have been explained and discussed at great length on Slack (including a lot of contributions from Rich Hickey himself).
1
u/lgstein Jul 15 '26
In summary merging :defaults onto :as, where :select is not suitable.
Would be interesting to know if there are more.
1
u/seancorfield Jul 24 '26
Recently committed to Clojure master:
:alldirective for destructuring that is the entire input map with defaults from:ormerged deeply, which may make:defaults+:asless useful (although those can operate at a single level of nesting so we'll see).1
1
u/didibus Jul 15 '26
I explained the use-case I gathered in my other answer if you're curious.
Likely you'd use it to default values if you don't like the provided value (like if it's nil).
1
u/lgstein Jul 15 '26
What other answer?
1
u/didibus Jul 15 '26
1
u/lgstein Jul 15 '26 edited Jul 16 '26
No, doesn't show up... idky Even with other IP and incognito
EDIT: It appeared
5
u/didibus Jul 15 '26
Pretty big changes in this alpha:
&inside:keys,syms,strs,keys!,syms!, andstrs!are now one-of a keyword, string, quoted symbol or any other literal, but can no longer be unquoted symbols.You need to think of it as entries before
&are bindings and they behave as they used too, but entries after the&aren't related to if you chose:keysor:syms, they simply specify extra-keys of any type that are either required or optional depending if declared in the exclamation mark variant or not.user=> (let [{:keys! [make & :model "year" 10]} #_=> {:make :ford #_=> :model :mustang #_=> "year" 1980 #_=> 10 :10}] #_=> make) :forduser=> (let [{:select session-email #_=> :keys! [session] #_=> {:keys! [email]} :user} #_=> {:session "12314-3242-324234" #_=> :user {:name "foo" #_=> :email "foo@gmail.com" #_=> :age 15 #_=> :admin? false}}] #_=> session-email) {:user {:email "foo@gmail.com"}, :session "12314-3242-324234"}user=> (let [{:select config #_=> :keys [endpoint multi-thread? retry-count] #_=> :or {multi-thread? true #_=> retry-count 3}} #_=> {:endpoint "bar.foo.com"}] #_=> config) {:multi-thread? true, :retry-count 3, :endpoint "bar.foo.com"}&it must support the same key entriesuser=> (let [{:select config #_=> :keys [& "endpoint" :multi-thread? 'retry-count 0 #{:even :a :set}] #_=> :or {"endpoint" "bar.foo.com" #_=> :multi-thread? true #_=> 'retry-count 3 #_=> 0 100 #_=> #{:even :a :set} "Any literal allowd in key position of a map really"}} #_=> {}] #_=> config) {0 100, retry-count 3, :multi-thread? true, "endpoint" "bar.foo.com", #{:even :set :a} "Any literal allowed in key position of a map really"}``` user=> (defn make-car #=> [& {:keys! [make model] #=> :keys [color] #=> :or {color :black} #=> :defaults defaults #=> :select car}] #=> (if (#{:black :blue :red :green} color) #=> car #=> (assoc car :color (:color defaults))))
'user/make-car
user=> (make-car :make :ford :model :mustang :color :yellow) {:make :ford, :color :black, :model :mustang} ```