关于clojure:cljx的目标相关宏

Target-dependent macros with cljx

问题描述

我有一个通过CLJX面向Clojure(JVM)和ClojureScript的项目。

我有一个接受thunk的宏,并创建一个IDeref实例,以在每次取消引用(使用deref@)时执行该thunk。

由于它是一个宏,因此必须放入.clj文件中。问题是IDeref接口对于Clojure和ClojureScript是不同的。在Clojure中,我需要生成以下内容:

1
2
(reify clojure.lang.IDeref
  (deref [_] thunk))

在ClojureScript中,我需要生成以下代码:

1
2
(reify IDeref
  (-deref [_] thunk))

由于这是一个宏,因此我无法使用cljx中类似featuer-expression的语法(例如#+cljs -deref)来协调我两个目标平台的代码。我最终要做的是:

1
2
3
(defmacro make-thunk [exp]
  `(reify ~params/IDeref-interface
     (~params/IDeref-method [_#] ~exp)))

然后,我在clj和cljs源代码树中都有一个单独的params.clj,每个树中每个需要的符号都有一个def

这行得通,但确实很丑陋,感觉就像是肮脏的骇客。

我的问题

我真的很想将所有宏都保留在相同的名称空间中。我宁愿不必在单独的文件中为宏定义每个与平台相关的符号。我已经在两个源代码树中有了依赖于平台的compat.clj和compat.cljs文件。必须添加更多文件以支持依赖于平台的宏开始使事情变得混乱。

有没有更清洁的解决方案来解决这个问题?


在宏主体内部,在ClojureScript中而不在Clojure中扩展时,(:ns &env)将是正确的。

当前,这是编写特定于平台的宏的"最佳实践":

1
2
3
4
(defmacro platform []
  (if (:ns &env)
    :CLJS
    :CLJ))


这应该可以解决问题:

1
2
3
4
5
6
7
8
9
(defn compiling-cljs?
 "Returns true if we seem to be compiling ClojureScript, false otherwise.

  This is a Clojure function, meant to be called by macros targeting both
  Clojure and ClojureScript."
  []
  (if-let [cljs-ns-var (resolve 'cljs.analyzer/*cljs-ns*)]
    (some? @cljs-ns-var)
    false))

像这样使用:

1
2
3
4
(defmacro foo []
  (if (compiling-cljs?)
    1
    2))

(我觉得这个问题以前已经解决过,但似乎找不到参考。)