3

问题描述

我有一个通过 CLJX 同时针对 Clojure (JVM) 和 ClojureScript 的项目

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

因为它是一个宏,所以它必须放在一个.clj文件中。问题是IDerefClojure 和 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用于每个需要的符号。

这行得通,但它真的很难看,感觉就像一个肮脏的黑客。

我的问题

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

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

4

2 回答 2

3

在宏的主体内部,(:ns &env)在 ClojureScript 中展开时将是真实的,但在 Clojure 中则不是。

这是目前编写特定于平台的宏的“最佳实践”:

(defmacro platform []
  (if (:ns &env)
    :CLJS
    :CLJ))
于 2015-02-27T07:08:13.470 回答
2

这应该可以解决问题:

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

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

于 2015-02-27T00:48:29.103 回答