store/[org.clojure/clojure "1.9.0"] clj::clojure.core/let

Community Documentation



[bindings & body]


Takes a vector of bindings or destructuring forms, followed by arbitrarily many forms which will be executed with the given bindings. Bindings are evaluated and established sequentially, and may refer to prevous bindings in the vector. The result of the last body form is retuned.


(defmacro let
  "binding => binding-form init-expr

  Evaluates the exprs in a lexical context in which the symbols in
  the binding-forms are bound to their respective init-exprs or parts
  {:added "1.0", :special-form true, :forms '[(let [bindings*] exprs*)]}
  [bindings & body]
     (vector? bindings) "a vector for its binding"
     (even? (count bindings)) "an even number of forms in binding vector")
  `(let* ~(destructure bindings) ~@body))

Example 1

(let [a (take 5 (range))
      {:keys [b c d] :or {d 10 b 20 c 30}} {:c 50 :d 100}
      [e f g & h] ["a" "b" "c" "d" "e"]
      _ (println "I was here!")
      foo 12
      bar (+ foo 100)]
  [a b c d e f g h foo bar])
;; > I was here!
;; => [(0 1 2 3 4) 20 50 100 "a" "b" "c" ("d" "e") 12 112]

Example 2

;;; map destructuring, all features
(let [;;Binding Map
      {:keys [k1 k2]        ;; bind vals with keyword keys
       :strs [s1 s2]        ;; bind vals with string keys
       :syms [sym1 sym2]    ;; bind vals with symbol keys
       :or {k2 :default-kw, ;; default values
            s2 :default-s, 
            sym2 :default-sym} 
       :as m}  ;; bind the entire map to `m`
      {:k1 :keyword1, :k2 :keyword2,  ;; keyword keys
       "s1" :string1, "s2" :string2,  ;; string keys
       'sym1 :symbol1,                ;; symbol keys
       ;; 'sym2 :symbol2              ;; `sym2` will get default value
  [k1 k2 s1 s2 sym1 sym2 m])  ;; return value

[:keyword1, :keyword2, 
 :string1, :string2,
 :symbol1, :default-sym, ;; key didn't exist, so got the default
 {'sym1 :symbol1, :k1 :keyword1, :k2 :keyword2, 
  "s1" :string1, "s2" :string2}]

;; remember that vector and map destructuring can also be used with 
;; other macros that bind variables, e.g. `for` and `doseq`

Example 3

;;; no value of a key
(let [{:keys [a b] :as m} (:x {})]
  [a b m])
;; => [nil nil nil]

;;; same as above
(let [{:keys [a b] :as m} nil]
  [a b m])
;; => [nil nil nil]

;;; similar case on Vector
(let [[a b :as v] nil]
  [a b v])
;; => [nil nil nil]

Example 4

;; let is a Clojure special form, a fundamental building block of the language.
;; In addition to parameters passed to functions, let provides a way to create
;; lexical bindings of data structures to symbols. The binding, and therefore 
;; the ability to resolve the binding, is available only within the lexical 
;; context of the let. 
;; let uses pairs in a vector for each binding you'd like to make and the value 
;; of the let is the value of the last expression to be evaluated. let also 
;; allows for destructuring which is a way to bind symbols to only part of a 
;; collection.

;; A basic use for a let:
(let [x 1] 
;; => 1

;; Note that the binding for the symbol y won't exist outside of the let:
(let [y 1] 
;; => 1

(prn y)
;; => java.lang.Exception: Unable to resolve symbol: y in this context (NO_SOURCE_FILE:7)

;; Another valid use of let:
(let [a 1 b 2] 
  (+ a b))
;; => 3

;; The forms in the vector can be more complex (this example also uses
;; the thread macro):
(let [c (+ 1 2)
      [d e] [5 6]] 
  (-> (+ d e) (- c)))
;; => 8

;; The bindings for let need not match up (note the result is a numeric
;; type called a ratio):
(let [[g h] [1 2 3]] 
  (/ g h))
;; => 1/2

Example 5

;; :as example.
;; :as names the group you just destructured, letting you refer to that term.

(let [[x y :as my-point] [5 3]]
  (println x y)
  (println my-point))
;; > 5 3
;; > [5 3]
;; => nil

;; equivalent to (and far better than)

(let [my-point [5 3]
      [x y]    my-point]
  (println x y)
  (println my-point))

Uses on crossclj