binding

added
1.0

ns
clojure.core

type
macro

(binding bindings & body)

binding => var-symbol init-expr

Creates new bindings for the (already-existing) vars, with the
supplied initial values, executes the exprs in an implicit do, then
re-establishes the bindings that existed before.  The new bindings
are made in parallel (unlike let); all init-exprs are evaluated
before the vars are bound to their new values.

                ;; Here are the definitions.
(defn mymax [x y]
  (min x y))

(defn find-max [x y]
  (max x y))

user=> (let [max mymax]
         (find-max 10 20))

20 ;let is ineffective outside current lexical scope


user=> (binding [max mymax]
         (find-max 10 20))

10 ;because max is now acting as min
            
                ;; As of Clojure 1.3, vars need to be explicitly marked as ^:dynamic in order for
;; them to be dynamically rebindable:

user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2

;; Within the scope of the binding, x = 2 and y = 3

user=> (binding [x 2 y 3]
         (+ x y))
5

;; But once you leave the binding's scope, x and y maintain their original
;; bindings:

user=> (+ x y)
2
            
                ;;Use t like a "template"

(declare ^:dynamic t)

(defn addt [] 
  (+ t 10))

(binding [t 1]
  (addt))
=> 11
            
                ; You can set! bindings. Useful in a stateful programming.
user=> (def ^:dynamic d)
#'user/d
user=> d
#<Unbound Unbound: #'user/d>
user=> (binding [d 0] (prn d) (set! d 1) (prn d))
0
1
nil
user=> d
#<Unbound Unbound: #'user/d>
            
                ; Speed test. recur is the preferred way.
user=> (def a (atom 0))
#'user/a
user=> (def ^:dynamic b)
#'user/b
user=> (def d)
#'user/d
user=> (time (loop [r 0] (when (< r 10000000) (recur (inc r)))))
"Elapsed time: 8.062612 msecs"
nil
user=> (time (dotimes [_ 10000000] (reset! a 1)))
"Elapsed time: 93.428704 msecs"
nil
user=> (time (binding [b 0] (dotimes [_ 10000000] (set! b 1))))
"Elapsed time: 484.331821 msecs"
nil
user=> (time (with-local-vars [w 0] (dotimes [_ 10000000] (var-set w 1))))
"Elapsed time: 490.598696 msecs"
nil
user=> (time (dotimes [_ 10000000] (def d 1)))
"Elapsed time: 2154.646688 msecs"
nil

            
                ;; You can modify the variable inside a binding, 
;; inside a let, you can't.

(def ^:dynamic z)

(binding [z nil]
  (doseq [x (range 4) y (range 4)]
    (set! z [x y]))
  z)

; => [3 3]

;; You can modify the variable inside a for, with dorun.

(binding [z nil]
  (dorun
    (for [x (range 4) y (range 4)]
      (set! z [x y])))
  z)

; => [3 3]
            
                ;; from stackoverflow http://stackoverflow.com/questions/1523240/let-vs-binding-in-clojure
;; let creates a lexically scoped immutable alias for some value.  
;; binding creates a dynamically scoped binding for some Var.

;; Dynamic binding means that the code inside your binding form and any code 
;; which that code calls (even if not in the local lexical scope) will see the new binding.

user> (def ^:dynamic x 0)
#'user/x

;; Lexical vs. dynamic binding:

user> (defn foo [] (println x))
#'user/foo
user> (binding [x 1] (foo))
1
nil
user> (let [x 1] (foo))
0
nil
            
                ;; Beware usage in ClojureScript around asynchronous calls, as the bound
;; var's original value will be re-established before the async code executes:

(def ^:dynamic *foo* nil)

(binding [*foo* :bar]
  (js/setTimeout
    (fn []
      *foo* ;;=> nil
      ))

;; Also beware *synchronous* usage inside `cljs.test/async`, as the bound
;; var's original value will not be re-established:

(ns my-ns
  (:require [cljs.test :refer-macros [async deftest is]]))

(def ^:dynamic *foo* nil)

(deftest my-test
  (async done
    (binding [*foo* :bar]
      (done))))

(deftest another-test
  (async done
    (is (nil? *foo*))))

;; FAIL in (another-test)
;; expected: (nil? *foo*)
;;   actual: (not (nil? :bar))

;; At time of writing, the stable ClojureScript version is 1.9.89.