仅在定义符号时如何使用符号

时间:2016-08-09 15:08:23

标签: clojure

我想执行一些依赖于某个var的Clojure代码,但只有在定义了var 时才执行。作为简化示例,只有在定义if时才能执行sym表单的正文:

(if (resolve 'sym) (println sym))

不幸的是,这不起作用。如果未定义sym,编译器仍会尝试解析它并抛出:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: sym in this context

通过阅读Rich Hickley的评论here,我发现这种行为是由于Clojure使用(主要是)单通道编译。然而,尽管有这么多意义,但在这种情况下显然会导致不良行为。

我可以通过在运行时强制执行符号解析来解决问题:

(if (resolve 'sym) (println (deref (resolve 'sym))))

但这是一个不受欢迎的黑客行为。有没有更好的方法,或者Clojure的read-eval模型是不可能的?

(为什么我需要这样做?我在profiles.clj中定义了多个可组合的配置文件。其中一个用于vinyasa,这使我能够方便地注入各种功能可访问的.名称空间。其他人加载各种其他实用程序库。在其他实用程序库的配置文件中,我想使用vinyasa注入最重要的函数,但仅在加载vinyasa时。我currently using是上述黑客的变种。)

2 个答案:

答案 0 :(得分:1)

(defn get-when-var-defined [sym]
  (when-let [var-for-sym (resolve sym)] 
    @var-for-sym))

(get-when-var-defined 'foo) ;;=> nil
(def foo 1)
(get-when-var-defined 'foo) ;;=> 1 

答案 1 :(得分:1)

@Michiel使用when-let推荐的方法是解决此问题的最佳方法。重要的是,通过使用let隐藏现有绑定的能力,您可以使条件几乎完全透明:

(when-let [foo (resolve 'foo)]
  (foo))
;;=> nil

(defn foo []
  :bar)

(foo)
;;=> :bar

(when-let [foo (resolve 'foo)]
  (foo))
;;=> :bar