(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.