问题说明
当 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)