Clojure可选定义

时间:2017-07-11 22:14:57

标签: clojure

在Python中,我可以做类似的事情:

fast_thing_available = True

try:
    import fast_thing
except ImportError:
    fast_thing_available = False

# snip

if fast_thing_available:
   default = fast_thing
else:
   default = slow_thing

在Clojure中可以做同样的事情吗? 我接下来尝试了,但失败了(例如仍需要导入):

(ns sample.ns)

(def ^:private long-adder-available (atom false))

(try
  (do
    (import 'java.util.concurrent.atomic.LongAdder)
    (swap! long-adder-available (constantly true)))
  (catch ClassNotFoundException e ()))

(when (true? @long-adder-available)
  (do
     ; Here I'm using LongAdder itself
  ))

即使IllegalArgumentException: unable to resolve classname: LongAdder本身不可用,代码也会引发LongAdder

1 个答案:

答案 0 :(得分:1)

正如@amalloy在评论中指出的那样,when中的代码无法编译。我不确定是否有办法重新编写代码以便编译。但是,可以避免完全编译它。 Clojure宏可用于从编译中排除代码。

宏可以尝试导入类,并且只有在成功使用类发出代码时才会导入。有更简单的方法可以检查类路径中是否存在类,但在编译时调用import很重要。这样代码可以使用简单的类名(如LongAdder)。

当应用于此问题时,解决方案可能类似于下面的示例。调用import的代码对eval等有点难看,但很难将非文字参数传递给import。如果此代码不需要通用,则可以对类名进行硬编码,并简化其他一些操作。

(ns sample.core)

(defmacro defn-if-class
  "If clazz is successfully imported, emits (defn name args then)
   Emits (defn name args else) otherwise."
  [clazz name args then else]
  (if (try (eval `(import ~clazz)) true (catch Exception e nil))
    `(defn ~name ~args ~then)
    `(defn ~name ~args ~else)))

(defn-if-class java.util.concurrent.atomic.LongAdder
  foo []
  ;; if class exists
  (doto (LongAdder.)
    (. increment)
    (. sum))
  ;; else
  "fallback logic in case the class is not in classpath")

(defn -main [& args]
  (println (foo)))

我应该提一下,答案很大程度上受到Jay Fields'博客文章" Clojure: Conditionally Importing"和this answer