【问题标题】:Clojure optional definitionsClojure 可选定义
【发布时间】:2017-07-12 23:35:46
【问题描述】:

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

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

【问题讨论】:

  • 所以你开始一个repl,你输入(import 'java.util.concurrent.NotAClass),你得到一个IllegalArgumentException?您的代码按我的预期工作。 ClassNotFoundException 被抓到了。
  • 另外注意,你的swap! 形式可以更好地表达为:(reset! long-adder-available true),你也可以更简洁地表达你的when(when @long-adder-available ...),并且在@987654332 下@ 和when,你不需要do
  • @Josh 我不同意您认为它按预期工作的评估。重要的不是import 表达式在运行时引发的ClassNotFoundException,而是when 中的代码在编译时引发的异常,无论它是否实际运行都会被编译。
  • @amalloy 哎呀!当我运行该示例时,我完全忘记了将类的用法实际放在when 块中。感谢您的捕获!你的结论是发帖人想要做的事情是不可能的吗?
  • 并非不可能,但它很粗糙、很难,而且通常不是 Clojure 中的工作方式。您基本上必须使用反射而不是正常引用该类。

标签: clojure


【解决方案1】:

正如@amalloy 在 cmets 中所指出的,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 的启发。

【讨论】:

  • 我在考虑一个宏,但我认为有更好的方法。谢谢。
猜你喜欢
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
相关资源
最近更新 更多