【问题标题】:How can I force evaluation of nested macros in Clojurescript?如何强制评估 Clojurescript 中的嵌套宏?
【发布时间】:2017-05-15 17:56:18
【问题描述】:

我有一个 CLJC 文件,它在 Clojure 中产生以下所需的输出:

(ns myproj.macros-ns)

(defmacro inner-macro [s]
  `['(my-ns/my-fn) :from ['~s :all]])

(defmacro outer-macro [y xs]
  `(into ['~y '~'<-] (eval '~xs)))

(defmacro macro-context [other-macros]
  (let [symbols (eval other-macros)
        _ (println "Expanded in macro context" symbols)]
    {:result `(list '~symbols '~'+ '~'further-rearrangement)}))


(macro-context (outer-macro ?sym-a (inner-macro ?sym-b)))

Expanded in macro context [?sym-a <- (my-ns/my-fn) :from [?sym-b :all]]

=> {:result ([?sym-a <- (my-ns/my-fn) :from [?sym-b :all]] + further-rearrangement)}

我的问题是:如何在 Clojurescript 中获得相同的结果?

我的 CLJS 文件如下所示:

(ns myproj.app-ns
   (:require-macros [myproj.macros-ns :refer [outer-macro
                                              inner-macro
                                              macro-context]]))

(enable-console-print!)

(macro-context (outer-macro ?sym-a (inner-macro ?sym-b)))

我得到的错误是:

clojure.lang.ExceptionInfo: java.lang.RuntimeException: Unable to 
resolve symbol: outer-macro in this context, compiling:
(/private/var/folders/2g/sfp74ftj6_q1vw51ytjbgvph0000gn/T/form-
init4244939051951953637.clj:13:3) at line 12 
test/macros/cljs/myproj/app_ns.cljs

我最终想要做什么以及为什么?

我正在编写一个包装https://github.com/cerner/clara-rules 的框架。 Clara 有自己的宏 defrule,它使用以下 DSL 语法

(defrule my-rule
 [?fact <- (my-ns/my-fn) :from [:all (= ?e (:e this))]
 ...

我有一个将以下内容扩展为上一个的宏:

(macro-context my-rule
 [?fact <- (my-ns/my-fn) :from [?e :all]]
...

执行此操作的宏基本上是上面更一般示例中的macro-context。当我只解析这样的语法时,我不会调用evalmacroexpand。我可以将所有内容都视为一个符号,将其重写为 Clara 的 DSL,然后将其传递给 defrule

这就是我认为失败的地方:

(macro-context
 [(outer-macro ?fact (inner-macro ?e))]
 ...

macro-context 宏内部,outer-macroinner-macro 未被评估,此时我需要它们的扩展。通过调用 eval,我可以在 Clojure 中得到它,但由于某种原因,在编译 Clojurescript 时,我收到“在这种情况下无法解析符号 outer-macro

【问题讨论】:

  • 这是一种非常奇怪的解决问题的方法,并且有点不清楚是什么实际问题推动了这个解决方案。您提供了“这是我试图做的一件事,但没有成功”,这让我想“是的,那肯定行不通,但我不知道还有什么建议,因为我没有”不知道你为什么要这样做”。您的解决方案对于 clj-jvm 来说也是错误的,而您只是巧合地侥幸逃脱。但同样,如果不知道在宏观时间生成这些奇怪形式的意义何在,我什至不能肯定地说。
  • 感谢您的回复!我将提供有关我要解决的问题的更多背景信息和一般信息。
  • 您应该使用macroexpand 而不是eval
  • @ClojureMostly 不,这不是一个正确的解决方案。如果fg 都是宏,那么(macroexpand '(f (g x)))(eval '(f (g x))) 不同(或者通常写(f (g x))):第一个不扩展g,而最后两个做。

标签: clojure macros clojurescript


【解决方案1】:

(outer-macro ?sym-a (inner-macro ?sym-b)) 形式传递到macro-context 时,outer-macroinner-macro 的(ClojureScript):refer 不会影响 Clojure 宏扩展。特别是,macro-context 中使用的eval 将无法解析这些符号。

但是如果你限定这些符号,比如说,用

(macro-context (myproj.macros-ns/outer-macro ?sym-a (myproj.macros-ns/inner-macro ?sym-b)))

然后一切都会好起来的。

更新

如果您像这样在宏定义中添加refer,则可以在 Clojure 中实现所需的引用:

(defmacro macro-context [other-macros]
  (refer 'myproj.macros-ns :only '[inner-macro outer-macro])
  (let [symbols (eval other-macros)
        _ (println "Expanded in macro context" symbols)]
    {:result `(list '~symbols '~'+ '~'further-rearrangement)}))

这样,inner-macroouter-macro 将在 Clojure *ns* 中引用,镜像您正在扩展的 ClojureScript ns。然后在 ClojureScript 中引用 macro-context 就足够了,并且符号可以解析。

【讨论】:

  • 谢谢!这行得通。非常感谢您的帮助,因为我在过去 2 天左右的时间里试图克服上述错误。但是......我现在想知道如何避免命名空间限定,因为预期用途是公共 DSL。我不希望消费者(包括我自己)不得不写类似 'my-project.my-namespace/
  • 在答案中添加了一个更新,显示了在 Clojure 端完成所需引用的一种方法,因此使用 ClojureScript 命名空间只需要引用 macro-context
【解决方案2】:

您正在尝试做的事情看起来应该非常简单易行,因为我们习惯于从函数的角度思考。函数可以很好地组合,您可以轻松地将一个大函数拆分为几个小函数,然后将每个函数的结果组合成一个更大的结果。宏实际上并没有这个属性:你必须制作一个巨大的泥球来一次做所有的事情,而把事情分解成更小的宏通常是行不通的。很不爽,你想做的其实还挺难的!

最直接的方法是屈服于泥球,可以这么说,因为 Clara 已经规定你必须在宏观领域完成所有有趣的逻辑。您可以声明,您的最顶层宏必须提前知道可能分解问题的所有方式,接受指示应该进行哪些转换的参数,然后自己进行这些转换,而不是将该任务委托给另一个宏(正如我们所说,你不能分解宏)。每当您需要引入一种新的灵活性时,编辑该“主”宏以向其添加另一个选项(将它们作为“选项”映射而不是位置参数可能是个好主意)。有点糟糕,但有时这就是生活。

一种更易于维护且初始成本更高的方法是尝试让自己回到函数领域:如上所述编写一个“主宏”,但将其分解为函数而不是宏。毕竟,宏只是一个接收源代码并返回源代码的函数,主要区别在于它是就地扩展的。您可以编写一个行为相同但未就地扩展的函数,并在扩展自身时让宏实现调用它。

遗憾的是,我真的没有时间写出我对这两种解决方案的意思的示例 sn-ps,部分原因是我很难直截了当地记住您提供的所有示例宏应该如何一起工作。但希望这堆散文仍然有用。

【讨论】:

  • 感谢您的回复!不过,我对此有点困惑。这种方法适用于 Clojure。抱歉,如果我之前没有说清楚。因此,Clojure 中的宏似乎可以使用上述方法进行组合。该问题仅在 Clojurescript 中出现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 2019-07-22
相关资源
最近更新 更多