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 会给我一个错误,说它无法解析我传入命名空间中的 vars。我猜这意味着在内部宏中,它成功地展开 ns-interns 来获取符号变量映射,但在编译过程中的后续步骤中将其销毁了。

因此,我现在确信,即使在 Clojurescript 中的编译时,也无法获取任何关于命名空间的信息,并使用这些信息生成代码。

0

在 CLJS 中,vars 并不会被直接在运行时实现。在宏中,你可以从分析器数据中获取它们。

你可以在 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*` 函数上是对编译器的私有,我认为我走错了方向——好像我的宏尝试评估它时 var 没有绑定。

如果没有链接,我会很感激任何关于编译器错误是什么,它对宏中的 "有效代码" 意味着什么的想法。

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

例如,即使在一个 .cljc 文件中要求 `cljs.env` 和 `cljs.compiler`,展开此代码会让 `x` 评估为 `nil`

(core/defmacro get-ns-interns [namespace]
   (core/let [x env/*compiler* ]
     `(let []
&         ~x
     )
&ebnbsp;   )
  )
您需要将数据从原子中提取出来,并使用这些数据生成所需的代码。实际上与 `ns-interns` 宏类似,在编译器项目内部或外部实现方式并无区别。如果您想“确保安全”,可以使用 `cljs.analyzer.api` 命名空间中的内容,而不是直接深入原子,但据我所知这样可以。
...