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 是一个符号到它们定义数据(例如,名称、元数据等)的映射。

ns-interns 本身上面构建将不会工作,但是使用其内部实现将允许你访问数据并生成你想要的内容。

谢谢。

你能否轻松访问类似这样的示例,但并非来自 *编译器项目*?当我尝试在 .clj 和 .cljc 文件中的宏中测试 `env/*compiler*` 时(使用 shadow-cljs),我遇到了“无法将 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
      )
    )
  )
您需要从原子中获取数据,并用这些数据生成您需要的任何代码。这与 `ns-interns` 宏类似,您在内部或外部编译器项目中执行它的方式没有区别。如果您想“安全一点”,请使用 `cljs.analyzer.api` 命名空间中的内容,而不是直接挖掘原子,但在我看来这样做是可以的。
...