问题陈述
当 clojure.core/deref
函数被非 clojure.lang.IDeref
或 java.util.concurrent.Future
实例的对象调用时,会抛出一个难以理解 ClassCastException
。在我的经验中,在接纳新 Clojure 用户(在我的工作和开源项目中),这种情况经常出现并且可能相当令人困惑,因为它没有在代码中指向任何明显的地方。即使有了堆栈跟踪,它仅指向 deref-future
的定义(而不是函数体中的某个特定行),这进一步增加了困惑。
讨论
在 Slack 上,我建议将 deref
的实现更改为类似 (cond (instance? clojure.lang.IDeref ref) ... (instance? java.util.concurrent.Future ref) ... :else (throw (IllegalArgumentException (str ref " cannot be deref'd as it is of type " (class ref) ".")))
的形式。
Sean Corfield 提到,这样的修复将影响所有非-IDeref 的 deref
使用性能,我承认这一点但觉得可以接受。
重现
user=> (def a {})
#'user/a
user=> @a
Execution error (ClassCastException) at user/eval22861 (REPL:0).
class clojure.lang.PersistentArrayMap cannot be cast to class java.util.concurrent.Future (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.util.concurrent.Future is in module java.base of loader 'bootstrap')
user=> (pst)
ClassCastException class clojure.lang.PersistentArrayMap cannot be cast to class java.util.concurrent.Future (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.util.concurrent.Future is in module java.base of loader 'bootstrap')
clojure.core/deref-future (core.clj:2315)
clojure.core/deref-future (core.clj:2315)
clojure.core/deref (core.clj:2338)
clojure.core/deref (core.clj:2323)
user/eval22863 (NO_SOURCE_FILE:0)
user/eval22863 (NO_SOURCE_FILE:-1)
clojure.lang.Compiler.eval (Compiler.java:7194)
clojure.lang.Compiler.eval (Compiler.java:7149)
clojure.core/eval (core.clj:3215)
clojure.core/eval (core.clj:3211)
clojure.main/repl/read-eval-print--9206/fn--9209 (main.clj:437)
clojure.main/repl/read-eval-print--9206 (main.clj:437)