Reading — step 1 of 7
Learn
Destructuring lets you bind names to parts of a data structure in one step. Pervasive in Clojure code. "Clojure for the Brave and True" treats it as everyday syntax.
Vector destructuring
(let [[a b c] [1 2 3]]
(println a b c))
;; 1 2 3
Positional binding. Skip elements with _:
(let [[_ b _] [1 2 3]]
b) ;; 2
Rest with &:
(let [[head & tail] [1 2 3 4 5]]
[head tail])
;; [1 (2 3 4 5)]
:as for the whole thing:
(let [[a b :as v] [1 2 3]]
[a b v])
;; [1 2 [1 2 3]]
Map destructuring
The most common pattern in Clojure:
(let [{:keys [name age]} {:name "Ada" :age 36}]
[name age])
;; ["Ada" 36]
:keys binds variables matching the key names. Cleaner than:
(let [m {:name "Ada" :age 36}
name (:name m)
age (:age m)]
[name age])
With defaults via :or:
(let [{:keys [name age role] :or {role :user}}
{:name "Ada" :age 36}]
[name age role])
;; ["Ada" 36 :user]
Renaming with positional binding:
(let [{user-name :name user-age :age} {:name "Ada" :age 36}]
[user-name user-age])
The key goes on the right of :, the bound name on the left.
Function arguments
Destructuring works in function parameters too:
(defn greet [{:keys [name age]}]
(str "Hi, " name ", you're " age))
(greet {:name "Ada" :age 36})
;; "Hi, Ada, you're 36"
Variadic args with destructuring:
(defn process [first & rest]
(println "first:" first)
(println "rest:" rest))
(process 1 2 3 4)
;; first: 1
;; rest: (2 3 4)
Nested destructuring
(let [{:keys [user]} {:user {:name "Ada" :role :admin}}
{:keys [name role]} user]
[name role])
Or inline:
(let [{{:keys [name role]} :user} {:user {:name "Ada" :role :admin}}]
[name role])
Readable when shallow; gets gnarly when deep. For deeply nested data, consider helper functions or specter (a Clojure library for nested data manipulation).
When to use destructuring
Yes:
- Binding parts of data with named meaning
- Function parameters that accept maps of options
- Iterating over key-value pairs
No:
- When you only use the whole value once
- When the structure is dynamic (use
get/get-in) - When destructuring becomes harder to read than direct access
Common mistakes
- Forgetting
:orfor defaults — without, missing keys bind to nil. Often want a default. - Mixing positional and map destructuring confusingly — keep the level simple.
- Deep nesting — readable for 1-2 levels, painful beyond. Use
get-inor a library. - Renaming syntax confusion —
{user-name :name}(rename) vs{:keys [name]}(use as-is). Different tools. - String keys —
{:keys [foo]}only works for keyword keys. For strings:{:strs [foo]}. For symbols:{:syms [foo]}.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…