关于这个话题的起源,可以在 Slack 上找到相关讨论 https://clojurians.slack.com/archives/C03S1L9DN/p1650480331191719
这段代码在 CLJ 中运行良好,但在 CLJS 中会触发 StackOverflowError
(def primes (remove
(fn [x]
(some #(zero? (mod x %)) primes))
(iterate inc 2)))
CLJ 和 CLJS 之间的关键区别在于,在 CLJ 中,lazy-seq
最终会将整个主体封装在一个带有 ^:once
的函数中。
这样做的推理是因为在代码中,内部的 primes
是一个封闭的值,它变成了局部变量。当 lazy-seq
的主体函数(带有 ^:once
)第二次被调用时,其局部变量为 nil
,因此内部 primes
也为 nil
。迭代停止。
在 CLJS 中,没有 :once
和局部变量清除,所以内部 primes
永远不会是 nil
,执行会不断进入那个 fn
而不调用那个 zero?
。
即使推理听起来不太可信,可以很容易地通过在 CLJ 中实现不带 :once
的 lazy-seq
并注意到这会导致 StackOverflowError
来证实这种差异是由于 :once
引起的假设。
我对此不确定要做什么,但也许至少应该在 https://script.clojure.org/about/differences 页面上添加关于它的注释。