当您对一个变量进行类型提示,例如
(def ^{:tag 'long} k 100)
并在let
绑定中引用它时
(let [i k]
(+ i 10))
绑定无法正确推断它是一个原始类型,正如其提示的那样
(set! *unchecked-math* :warn-on-boxed)
(def ^{:tag 'long} k 100)
(let [i k]
(+ i 10))
;;=> Boxed math warning, unchecked_add(java.lang.Object,long).
这看起来可能是由编译器中的错误引起的,由于某种原因,变量k的表达式被标记为没有Java类,尽管它有一个
(defmacro inspect-local []
(println
(into {}
(map (fn[[k v]]
[k {:tag (.-tag v)
:class (.getJavaClass v)
:primitive? (.isPrimitive (.getJavaClass v))
:has-java-class? (.hasJavaClass v)}]))
&env)))
(let [i k]
(inspect-local)
(+ i 10))
;;=> {i {:tag nil, :class long, :primitive? true, :has-java-class? false}}
正如我们所见,.hasJavaClass
返回假,但 .getJavaClass
返回 Long/TYPE
类。
我怀疑编译器在调用 .getJavaClass
之前检查 .hasJavaClass
,由于 .hasJavaClass
在这里错误地返回假,导致编译器认为它没有,类型不能推断。
我不太确定在编译器的哪个位置,但是我找到了以下内容: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341
我们在这里可以看到编译器首先检查的是 .hasJavaClass
if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())