使用 cljx 的目标相关宏

Target-dependent macros with cljx

问题描述

我有一个通过 CLJX.

同时针对 Clojure (JVM) 和 ClojureScript 的项目

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

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

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

在 ClojureScript 中我需要生成这个:

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

由于这是一个宏,我无法使用 cljx 中的类似特征表达式的语法(例如 #+cljs -deref)来协调我的两个目标平台的代码。这是我最后做的:

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

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

这行得通,但它真的很丑,感觉像是一个肮脏的 hack。

我的问题

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

这个问题有更简洁的解决方案吗?

这应该可以解决问题:

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

这样使用:

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

(我感觉这个问题之前已经解决了,但是好像找不到参考。)

在宏的主体内,(:ns &env) 在 ClojureScript 中扩展时为真,但在 Clojure 中不为真。

这是目前用于编写平台特定宏的 "best practice":

(defmacro platform []
  (if (:ns &env)
    :CLJS
    :CLJ))