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中,如何在编译时获取和操作命名空间的vars并解决这个问题呢?

(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 自身构建不会起作用,但是使用它内部的做法可以让您访问这些数据并生成您想要的东西。

谢谢。

您能否快速访问一个类似的项目之外的示例?当我在 shadow-cljs 中的 .clj 和 .cljc 文件中的宏中尝试测试 `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
      )
    )
  )
您需要从原子中提取数据,并使用该数据生成所需的任何代码。与`ns-interns`宏类似,在编译器项目内部或外部执行的方式并没有区别。如果您想保持在“安全的一边”,请使用`cljs.analyzer.api`命名空间中的内容,而不是直接挖掘原子,但这样做在我看来是可以接受的。
...