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

欢迎!有关更多信息,请参阅 关于 页面。

0
ClojureScript

我正在 clojurescript 中工作,其中 ns-interns 是一个宏。

我试图编写一个宏,该宏获取关于命名空间内部绑定的信息,但在编译时无法操纵绑定。

(defmacro my-macro [namespace]
   (let [mappings (ns-interns namespace)]
           ; do stuff
       ))

(my-macro 'mylib.hi)

麻烦在于,上面那个失败是因为 ns-interns 宏认为它正在处理符号 namespace,而不是传递给 my-macro 的命名空间。

我可以尝试

[mappings `(ns-interns ~namespace)]

,但这样我就不能在编译时展开宏到一个我可以用来生成代码的映射中 -- 我能做的事情只是在生成的代码中“打印”它。我试过使用 macroexpandmacroexpand-all,但对我没有用 -- 我无法使用它们来获取 ns-interns 的结果,以在我的宏中使用。

然而,宏是可以展开的,因为以下这样的代码如果能将命名空间符号硬编码进去,将能工作。问题在于因为我想要将外宏的参数传递给内宏。在这种情况下,正确的解决方法是什么,在 Clojurescript 中我想要在编译时获取并操纵命名空间的变量呢?

(defmacro my-macro [namespace]
   (let [mappings (ns-interns 'mylib.hi)]
     ; do stuff
   )
)

2 个答案

0

编辑了

我认为这是不可能的。我能做到的最近的是类似这样

(defmacro example-macro [ns]
  (let [x (list 'cljs.core/ns-interns ns)
        ]
    `(defmacro ~(symbol "example") []
       (let [y# ~x]
         5
         )
       )))

然后从某个其他命名空间调用 `(example-macro 'my.ns)` 来生成内部宏。但然后Clojure给我一个错误,说它无法解析我传入的命名空间中的变量。我猜想这意味着在内部宏中,它成功地将`ns-interns`展开来获取符号变量映射,但在编译过程中的后续步骤中使其失效。

所以我现在确信,在Clojurescript中,即使在编译时,也无法获取关于命名空间的数据并将其用于生成代码。

0

在CLJS中,变量不是在运行时直接实例化的。在宏中,您可以从分析器数据中获取它们。

您可以在ns-interns宏本身中找到一个如何实现的示例。在您的宏中需要的关键行是(get-in @env/*compiler* [:cljs.analyzer/namespaces ns :defs])

:defs是一个将符号映射到它们的def数据(例如名称,元数据等)的映射。

ns-interns本身之上构建将不会工作,但是使用它内部的做法可以使您访问数据并生成所需的内容。

谢谢。

您能否快速获取一个类似这样的示例,这个示例是来自编译器项目 *外部* 的?当我尝试在 .clj 和 .cljc 文件(使用 shadow-cljs)中测试宏中的 `env/*compiler*` 时,我遇到了像“无法将 clojure.lang.Atom 编译为 ClojureScript 常量”这样的错误,或者它将评估为 `nil`。当我尝试测试 `@env/*compiler*` 时,编译器会报告空指针错误。

由于 [env.cljc](https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/env.cljc) 表示 `env/*compiler* 是函数上私有的,我怀疑我可能找错了方向——就像我的宏尝试评估它时变量没有绑定一样。

如果没有链接,我会很欣赏任何关于编译错误对宏中“有效代码”意味着什么的看法。

----------------------------

例如,即使在 .cljc 文件中引入 `cljs.env` 和 `cljs.compiler`,展开此代码会使 `x` 评估为 `nil`。

(core/defmacro get-ns-interns [namespace]
  (core/let [x env/*compiler* ]
    `(let []
       ~x
      )
      )
  )
by
您需要从原子中获取数据,并使用这些数据生成所需的任何代码。这与 `ns-interns` 宏一样真正,在编译器项目内部或外部执行的方式没有区别。如果您想保持在“安全的一边”,请使用 `cljs.analyzer.api` 命名空间中的内容,而不是直接挖入原子,但我认为这样做是可以的。
...