Clojure dynamic var binding does not work as expected

From what I understand, setting a new binding for dynamic var affects all functions called inside that binding and all functions called from these functions.

Why is the binding apparently lost in the first example below?

(def ^:dynamic *out-dir* "/home/user")

(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives:    ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")

(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")
+4
source share
3 answers

This is caused by laziness - mapreturns a lazy sequence that is defined inside the binding, but evaluated externally. You need to force an evaluation from within:

(binding [*out-dir* "/home/dave"] 
  (doall (map #(str *out-dir* %) [1 2 3])))
+5
source

, ; - . ( pmap), bound-fn bound-fn*.

(def ^:dynamic x 0)

=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)

=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)

=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
+2

Another solution is to use Python-style generator functions available through lazy-genand yield from the Tupelo library :

(ns tst.demo.core
  (:use demo.core tupelo.test)
  (:require
    [tupelo.core :as t] ))
(t/refer-tupelo)

(def ^:dynamic foo 1)

(dotest
  (let [result (binding [foo 3]
                 (lazy-gen
                   (doseq [x (range 3)]
                     (yield {:foo foo :x x})))) ]
    (println result)))

result => ({:foo 3, :x 0} 
           {:foo 3, :x 1}
           {:foo 3, :x 2})
0
source

Source: https://habr.com/ru/post/1614270/


All Articles