簡體   English   中英

如何在Clojurescript中強制評估嵌套宏?

[英]How can I force evaluation of nested macros in 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基本上是macro-context 當我只解析這樣的語法時,我不調用evalmacroexpand 我能夠將所有內容視為符號,將其重寫為Clara的DSL,然后將其傳遞給defrule

我認為這是分解的地方:

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

macro-context宏內部,未對outer-macroinner-macro進行評估,這時我需要對其進行擴展。 通過調用eval,我可以在Clojure中得到它,但是由於某種原因,在編譯Clojurescript時,我收到“無法在這種情況下解析符號outer-macro

當將形式(outer-macro ?sym-a (inner-macro ?sym-b))傳遞到macro-context ,(ClojureScript) :refer for outer-macroinner-macro不會影響Clojure inner-macro擴展。 特別是,在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就足夠了,並且可以解析符號。

您嘗試做的事情看起來應該很簡單,因為我們習慣於在功能方面進行思考。 函數可以很好地組合,您可以輕松地將一個大函數分解為幾個小函數,然后將每個結果組合為一個更大的結果。 宏實際上並沒有這種特性:您必須精加工一個巨大的泥球才能立即執行所有操作,將它們分解成較小的宏通常是行不通的。 因此,不幸的是,您實際上想做的事很難!

可以這么說,最直接的方法就是屈服於泥球,因為克拉拉(Clara)規定您必須在大地上做所有有趣的邏輯。 您可以聲明最頂層的宏必須提前知道所有可能解決問題的方式,接受指示應該進行哪些轉換的參數,然后自行進行這些轉換,而不是將任務委托給另一個宏(自從我們說過以來,您不能分解一個宏)。 每當您需要引入一種新的靈活性時,都可以編輯“ master”宏以向其添加另一個選項(將它們作為“ options”映射而不是位置參數可能是一個好主意)。 Kinda很爛,但有時就是生活。

一種具有較高初始成本的可維護性更高的方法是嘗試使自己重新回到功能領域:如上所述編寫一個“主宏”,但將其分解為函數而不是宏。 畢竟,宏只是一個接收源代碼並返回源代碼的函數,其主要區別在於它是就地擴展的。 您可以編寫一個行為相同但未適當擴展的函數,並在擴展自身時讓宏實現調用它。

可悲的是,我真的沒有時間寫出我對每種解決方案的含義的示例代碼片段,部分原因是很難直截了當地弄清楚您提供的所有示例宏應該如何協同工作。 但是希望這堆散文仍然被證明是有用的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM