在 Clojure 中,我将如何为惰性序列重新定义 vars?

In Clojure, how would I redefine vars for a lazy sequence?

在命名空间中,我有两个动态变量:

(def ^:dynamic *form-data*)
(def ^:dynamic *form-errors*)

为了快速为它们创建新的绑定,我制作了一个包装器宏:

(defmacro with-form [data errors & body]
  `(binding [*form-data*    ~data 
             *form-errors*  ~errors]
     ~@body))

我在同一个命名空间中有几个依赖这些变量的函数,其中之一是 input-field

当我在 repl 中单独使用该函数时,它会起作用。但是当我这样使用它时:

(vf/with-form {} {}
  (map #(vf/input-field hf/text-field %) [:name :code]))

我得到一个错误:Attempting to call unbound fn: *form-errors*

我猜问题是 bindings 运行了 map,创建了一个惰性序列,并将变量解除绑定回到它们的原始状态。有没有办法绕过这个限制?

谢谢。

使用doall强制实现整个lazy seq:

(vf/with-form {} {}
  (doall (map #(vf/input-field hf/text-field %) [:name :code])))

binding 文档中的这个 example 说明了相同的技术。

或者,要保持惰性,请使用 bound-fn*,如文档中的 example 所示:

(vf/with-form {} {}
  (map (bound-fn* #(vf/input-field hf/text-field %)) [:name :code]))

只是为了补充之前的答案,请参阅此 list of documentation sources,尤其是 Clojure CheatSheet。

特别是,我总是喜欢使用mapv而不是map。相当于

(vec (map ...))

它避免了惰性序列造成的“陷阱”。