2024 Clojure 状态调查! 中分享你的想法。

欢迎!请查看 关于 页面以获取更多关于如何使用本网站的信息。

+1 投票
编译器

为什么我不能像这样在函数中使用 require?

(comment
  (->> [1 2]
       (reduce (fn [acc v]
                 (require '[clojure.set :as c-set])
                 (c-set/union acc #{v}))
               #{})) ;=> Syntax error compiling at (lab/sandbox_playground.clj:31:18).
                     ;   No such namespace: c-set
  )

最初我以为 require 需要在顶层进行。但是:这也可以工作

(comment
  (do
    (require '[clojure.set :as c-set])
    (c-set/union #{1} #{2})) ;=> #{1 2}
  )

2 答案

+3 投票

被选中
 
最佳答案

为什么不直接使用 requiring-resolve 呢?

编辑,为了更详细的信息

(comment
  (->> [1 2]
       (reduce (fn [acc v]
                 ((requiring-resolve 'clojure.set/union) acc #{v}))
         #{}))
  ;;=> #{1 2}
  )

正如在与 Peter (op) 线下交谈时所了解的,那块代码无法工作的原因是被称为 Gilardi 场景。有关该问题的详细描述可以在 ClojureDocs 中找到。

当可能时, prefers requiring-resolve 另一个原因是, require 在 Clojure 1.11.1 中不是线程安全的。有关此信息的更多详情可以在(在其他人中) 此问题 中找到,以及 ClojureDocs 中的 这里这里

是的,为什么不呢?你能详细说明一下吗?
(评论
      (->> [1 2]
           (reduce (fn [acc v]
                   ((requiring-resolve 'clojure.set/union) acc #{v}))
             #{}))
      ===> #{1 2}
      )

重新展示
另外一点:tools.build大量使用requiring-resolve,因此我认为它是此类问题的官方解决方案,除非该函数的语义不适合当前情况:https://github.com/clojure/tools.build/blob/a9d0804ce8014088a7388e762034e9013922cacb/src/main/clojure/clojure/tools/build/api.clj#L126
+1 投票

修改

我试了这样

(defmacro require-macro [arg]
  (require arg))

(->> [1 2]
 (reduce (fn [acc v]
           (require-macro [clojure.set :as c-set])
           (c-set/union acc #{v}))
         #{}))

我认为当lambda被读取/编译时,它不会执行。要在编译时执行,也许可以调用一个宏'function'。

这实际上是有效的。我不太理解为什么。你能解释一下吗?
当lambda定义被编译,作为一个函数,它已经执行了0次。因此,在第二行使用它之前,还没有进行require操作。(并且我们仍在编译时。)编译器需要知道什么是'c-set'。
我想我已经理解了那些。但是我不明白为什么把require包裹在一个宏中会使`c-set`的使用有效。
唯一的区别在于它是在宏展开时执行的。(宏通常是将代码转换为代码的函数,但在这个情况下,它用于触发 require 的副作用。)如果你想要构建带有此类定义的 AOT 编译的库,这可能不是你的问题的解决方案。它在 REPL 中也可以正常工作。

注意:我并不完全理解你的最终用途。
谢谢。我猜我需要认真想想宏展开的时间。=)

我的用法是在 ClojureScript (或者更准确地说是在 nodejs 环境中的 SCI) 中,我需要推迟 require 直到一个承诺解析。我还没有在那里尝试这个解决方案,但会尝试并报告效果。现在,我正使用 @Imre 的解决方案,这对我来说是很好的,但我仍然想尝试一下这个。
对不起,但我的伪宏的修复在这个案例中将没有用处,尽管它在 REPL 中产生了好结果。Imre 的才是对的。
实际上,在我的用法中它是可以工作的。顺便说一句。(我还是依靠 Imre 的解决方案,但无论如何。)
...