如何在Clojurescript中强制评估嵌套宏?

时间:2017-05-15 17:56:19

标签: 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 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时,我收到了#34;在此上下文中无法解析符号outer-macro

2 个答案:

答案 0 :(得分:2)

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

但是,如果您使用

来限定这些符号
(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就足够了,符号就会解决。

答案 1 :(得分:0)

你想要做的事情看起来应该是非常简单和容易的,因为我们习惯于在功能方面进行思考。函数可以很好地组合,你可以很容易地将一个大函数分成几个小函数,然后将每个函数的结果组合成一个更大的结果。宏并没有真正拥有这种特性:你必须制造一个巨大的泥球,一次完成所有事情,而把事情分解成更小的宏并不常见。所以不幸的是,你想要做的事实上是相当困难的!

最直接的方法是屈服于泥球,可以这么说,因为克拉拉已经断言你必须在宏观土地上做所有有趣的逻辑。您可以声明最顶层的宏必须提前知道问题可能被分解的所有方式,接受指示应该进行哪些转换的参数,然后自己进行这些转换,而不是将该任务委派给另一个宏(因为正如我们所说,你不能分解宏)。任何时候你需要引入一种新的灵活性,编辑&#34; master&#34;宏为它添加另一个选项(可能最好将它们作为&#34;选项&#34;映射而不是位置参数)。有点糟透了,但有时候这很糟糕。

更具可维护性的方法,更高的初始成本将是试图让自己回到功能的土地:写一个&#34;主宏&#34;如上所述,但将其分解为 functions 而不是宏。毕竟,宏只是一个接受源代码并返回源代码的函数,其关键区别在于它是就地扩展的。您可以编写一个行为相同但未扩展的函数,并在扩展自己时让宏实现调用它。

可悲的是,我没有时间写出我对任何一个解决方案的意思的示例片段,部分原因是我很难直截了当地知道你提供的所有示例宏是如何一起工作。但是,希望这一堆散文证明有些有用。