从Clojure的核心库中有条件地绑定一个动态变量以实现向后兼容性。

3
我已经尝试了一段时间来解决这个nREPL问题,但是我已经没有思路了,所以我决定在这里寻求一些帮助。
基本上,我们只需要绑定*print-namespace-maps*,如果它存在的话,或者找到一些定义它不存在的方式,而不会弄乱Clojure 1.9+。
以下是一些行不通的方法:
;; compilation error
(when (resolve '*print-namespace-maps*)
  (set! *print-namespace-maps* (@bindings #'*print-namespace-maps*)))

;; shadows `clojure.core/*print-namespace-maps*` on 1.9, as the def gets executed always
(when-not (resolve 'clojure.core/*print-namespace-maps*)
  (def ^:dynamic *print-namespace-maps*
    "Define the var for clojure versions below 1.9"
    nil))

似乎动态变量无法有条件地绑定,这非常糟糕,但我想一定有某种方法可以实现我们的目标。

有关更多详细信息,请参阅链接的问题。非常感谢任何帮助!

1个回答

2

我并不完全理解链接问题或nREPL实现的细节,但def的行为在这里有点令人惊讶:

Clojure 1.8.0
user=> (when false (def ^:dynamic *foo* 1))
nil

我本来以为在那个评估之后不会有*foo*变量,但实际上它创建了一个未绑定的变量:

user=> (var-get #'*foo*)
#object[clojure.lang.Var$Unbound 0x20da8800 "Unbound: #'user/*foo*"]

我猜测你举的1.9版本中的例子中,def编译器的行为是导致异常的原因:

WARNING: *print-namespace-maps* already refers to: #'clojure.core/*print-namespace-maps* in namespace: user, being replaced by: #'user/*print-namespace-maps*

也许可以使用宏来实现在宏扩展期间进行resolve检查,并且如果解析了,则不会发出def
(defmacro def-dynamic-when-not-resolve [sym value]
  (when-not (resolve sym)
    `(def ~(vary-meta sym assoc :dynamic true) ~value)))

看起来可以在Clojure 1.8上运行:

Clojure 1.8.0
user=> (def-dynamic-when-not-resolve *print-namespace-maps* 'sure)
#'user/*print-namespace-maps*
user=> *print-namespace-maps*
sure

关于Clojure 1.9:

Clojure 1.9.0
user=> (def-dynamic-when-not-resolve *print-namespace-maps* 'sure)
nil
user=> *print-namespace-maps*
true

def 是编译器的 Java 部分。似乎存在读取或编译时的副作用,但我一开始没能发现它。 - Svante
是的,我忘记将def作为Clojure的少数特殊形式之一进行调用。 - Taylor Wood
好的,那我们就选择这个选项吧!谢谢! - Bozhidar Batsov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接