问题描述
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 “无法 deref 为它当前类型为” (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)