destructure []

bindingsince v0.0-927 in clojureEdit

Shorthand for destructuring a sequence into multiple names. Allows you to use a [...] pattern instead of a usual name in the following examples:

  • (defn [name]) -> (defn [[...]])
  • (let [name sequence]) -> (let [[...] sequence])
  • (for [name sequences]) -> (for [[...] sequences])

Quick pattern reference:

  • [...] - names are bound by order (e.g. [a b])
  • [... & name] - name is bound to the rest of the values
  • [:as name] - name is bound to whole value

Details:

A helpful shorthand for destructuring a sequence into multiple names.

(let [ [a b c]   ;; <-- destructure vector
       [1 2 3] ]
  (println a b c))
;; 1 2 3

Use & foo to name the rest of the items in the sequence:

(let [ [a b c & d]
       [1 2 3 4 5] ]
  (println a b c d))
;; 1 2 3 (4 5)

Use :as foo to name the original value:

(let [ [a b c & d :as whole]
       [1 2 3 4 5] ]
  whole)
;;=> [1 2 3 4 5]

Use the special destructure vector in place of any local name binding in the following forms:

  • (let [...])
  • (fn [...])
  • (loop [...])

Destructure vectors can be nested, even in place of names in destructure maps.


Examples:

Use destructure vectors in function parameters:

(defn foo [[a b] c]
  (+ a b c))

(foo [1 2] 3)
;;=> 6

Destructure vectors can be nested:

(let [ [[a b] c]
       [[1 2] 3] ]
  (println a b c))
;; 1 2 3

See Also:


Parser code @ clojurescript:src/main/clojure/cljs/core.cljc
(core/defn destructure [bindings]
  (core/let [bents (partition 2 bindings)
             pb (core/fn pb [bvec b v]
                  (core/let [pvec
                             (core/fn [bvec b val]
                               (core/let [gvec (gensym "vec__")
                                          gseq (gensym "seq__")
                                          gfirst (gensym "first__")
                                          has-rest (some #{'&} b)]
                                 (core/loop [ret (core/let [ret (conj bvec gvec val)]
                                                   (if has-rest
                                                     (conj ret gseq (core/list `seq gvec))
                                                     ret))
                                             n 0
                                             bs b
                                             seen-rest? false]
                                   (if (seq bs)
                                     (core/let [firstb (first bs)]
                                       (core/cond
                                         (= firstb '&) (recur (pb ret (second bs) gseq)
                                                              n
                                                              (nnext bs)
                                                              true)
                                         (= firstb :as) (pb ret (second bs) gvec)
                                         :else (if seen-rest?
                                                 (throw #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter")
                                                           :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter")))
                                                 (recur (pb (if has-rest
                                                              (conj ret
                                                                    gfirst `(first ~gseq)
                                                                    gseq `(next ~gseq))
                                                              ret)
                                                            firstb
                                                            (if has-rest
                                                              gfirst
                                                              (core/list `nth gvec n nil)))
                                                        (core/inc n)
                                                        (next bs)
                                                        seen-rest?))))
                                     ret))))
                             pmap
                             (core/fn [bvec b v]
                               (core/let [gmap (gensym "map__")
                                          defaults (:or b)]
                                 (core/loop [ret (core/-> bvec (conj gmap) (conj v)
                                                          (conj gmap) (conj `(--destructure-map ~gmap))
                                                     ((core/fn [ret]
                                                        (if (:as b)
                                                          (conj ret (:as b) gmap)
                                                          ret))))
                                             bes (core/let [transforms
                                                            (reduce
                                                              (core/fn [transforms mk]
                                                                (if (core/keyword? mk)
                                                                  (core/let [mkns (namespace mk)
                                                                        mkn (name mk)]
                                                                    (core/cond (= mkn "keys") (assoc transforms mk #(keyword (core/or mkns (namespace %)) (name %)))
                                                                               (= mkn "syms") (assoc transforms mk #(core/list `quote (symbol (core/or mkns (namespace %)) (name %))))
                                                                               (= mkn "strs") (assoc transforms mk core/str)
                                                                               :else transforms))
                                                                  transforms))
                                                              {}
                                                              (keys b))]
                                                   (reduce
                                                     (core/fn [bes entry]
                                                       (reduce #(assoc %1 %2 ((val entry) %2))
                                                         (dissoc bes (key entry))
                                                         ((key entry) bes)))
                                                     (dissoc b :as :or)
                                                     transforms))]
                                   (if (seq bes)
                                     (core/let [bb (key (first bes))
                                                bk (val (first bes))
                                                local (if #?(:clj  (core/instance? clojure.lang.Named bb)
                                                             :cljs (cljs.core/implements? INamed bb))
                                                          (with-meta (symbol nil (name bb)) (meta bb))
                                                        bb)
                                                bv (if (contains? defaults local)
                                                     (core/list 'cljs.core/get gmap bk (defaults local))
                                                     (core/list 'cljs.core/get gmap bk))]
                                       (recur
                                         (if (core/or (core/keyword? bb) (core/symbol? bb)) ;(ident? bb)
                                           (core/-> ret (conj local bv))
                                           (pb ret bb bv))
                                              (next bes)))
                                     ret))))]
                    (core/cond
                      (core/symbol? b) (core/-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v))
                      (core/keyword? b) (core/-> bvec (conj (symbol (name b))) (conj v))
                      (vector? b) (pvec bvec b v)
                      (map? b) (pmap bvec b v)
                      :else (throw
                             #?(:clj (new Exception (core/str "Unsupported binding form: " b))
                                :cljs (new js/Error (core/str "Unsupported binding form: " b)))))))
             process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))]
    (if (every? core/symbol? (map first bents))
      bindings
      (core/if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))]
        (throw
          #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs)))
             :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs)))))
        (reduce process-entry [] bents)))))