如何强制计算Clojurescript中的嵌套宏?

如何强制计算Clojurescript中的嵌套宏?,clojure,macros,clojurescript,Clojure,Macros,Clojurescript,我有一个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 o

我有一个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)}
我得到的错误是:

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
我最终想做什么?为什么?

我正在写一个框架来包装。Clara有自己的宏,
defrule
,它使用以下DSL语法

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

宏上下文
宏中,
外部宏
内部宏
未计算,此时我需要它们的扩展。通过调用eval,我可以在Clojure中得到它,但由于某种原因,在编译Clojurescript时我收到了它“在此上下文中无法解析symbol
外部宏

您尝试执行的操作看起来应该非常简单和容易,因为我们习惯于从函数的角度进行思考。”。函数具有良好的可组合性,您可以轻松地将一个大函数拆分为几个小函数,然后将每个函数的结果组合为一个较大的结果。宏并没有这样的特性:你必须制造一个巨大的泥球来同时完成所有的事情,而把东西分解成更小的宏通常是不起作用的。所以不幸的是,你想要做的其实是相当困难的

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

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

遗憾的是,我没有时间写出我对这两种解决方案的意思的示例片段,部分原因是我很难清楚地知道您提供的所有示例宏是如何协同工作的。但是,希望这堆散文还是有一定的用处。

当形式
(外部宏?sym-a(内部宏?sym-b))
被传递到
宏上下文中时,(ClojureScript)
:请参阅
以了解
外部宏
内部宏
不会影响Clojure宏扩展。特别是,在
宏上下文中使用的
eval
将无法解析这些符号

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

(macro-context (myproj.macros-ns/outer-macro ?sym-a (myproj.macros-ns/inner-macro ?sym-b)))
然后事情就会发生了

更新:

如果在宏定义中添加
reference
,则可以在Clojure中实现所需的引用,如下所示:

(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)))
(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)}))

这样,
内部宏
外部宏
将在Clojure
*ns*
中引用,镜像从中展开的ClojureScript ns。然后,在ClojureScript中引用
宏上下文
就足够了,符号可以解决问题。

这是一种非常奇怪的解决问题的方法,现在还不清楚是什么实际问题促使了这种解决方案。您提供了“这是一件我试图做的事情,但没有成功”,这让我想“是的,这肯定不会成功,但我不知道还有什么建议,因为我不知道您为什么要这样做”。对于CLJJVM,您的解决方案看起来也是错误的,您只是碰巧侥幸逃脱了。但是,如果我不知道在宏时间生成这些奇怪的表单的意义,我甚至不能肯定这一点。谢谢您的回复!我将提供有关我试图解决的问题的更多上下文和一般信息。您应该使用
macroexpand
而不是
eval
@clojure事实上,这不是正确的解决方案。如果
f
g
都是宏,那么
(macroexpand'(f(gx))
不同于
(eval'(f(gx))
(或者通常的事情,简单地写
(f(gx))
:第一个不会展开
g
,而最后两个会展开。感谢您的回复!不过我有点困惑。这种方法在Clojure中有效。对不起,如果我之前没有说清楚的话。因此,Clojure中的宏似乎可以使用上述方法进行组合。问题只在Clojurescript中出现。谢谢!这很有效。我非常感谢你的帮助,因为我在过去的两天左右都在努力克服上面的错误。但是…我现在想知道如何避免名称空间限定,因为预期用途是公共DSL。我不希望使用者(包括我自己)必须编写类似于“my-project.my-namespace/的内容,在答案中添加了一个更新,它显示了在Clojure端完成所需引用的一种方法,以便使用者ClojureScript命名空间只需要引用
宏上下文
(macro-context (myproj.macros-ns/outer-macro ?sym-a (myproj.macros-ns/inner-macro ?sym-b)))
(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)}))