binding | since v0.0-927 | in clojure | Edit |
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 valueA 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.
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
(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)))))