请在2024 Clojure调查问卷!中分享您的想法。

欢迎!请参阅关于页面,了解更多关于该服务的信息。

+1
编译器

当你对一个如下的变量进行类型标注

(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返回false,而.getJavaClass返回Long/TYPE类。

我怀疑编译器在调用.getJavaClass之前会先检查.hasJavaClass,由于.hasJavaClass在这里错误地返回false,它会导致编译器认为它没有Java类,而且无法推断类型。

我不是很清楚这个问题在编译器中的具体位置,但我找到了以下信息:https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341

我们在这里可以看到编译器首先检查.hasJavaClass

 if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
据我所知,原始类型提示仅在局部绑定中有效。从变量类型提示中推断它们不支持。我不确定它在文档中的位置,所以仍需查找。
找到了,已作为答案发布。

2 个答案

+1
by

文档中只提到支持“局部作用域”作为原始类型提示

https://clojure.org/reference/java_interop#primitives

对于原始类型提示,不支持var(返回类型)上的标签。

+1
by

无论此处类型提示为何种类型,vars始终是Objects,示例使用中将是一个boxed值(因此不是原始类型)。然而,如果标记为^:const和一个原始类型,编译器将以内联var的形式在let中使用原始long,在这种情况下它将是原始类型。

(set! *unchecked-math* :warn-on-boxed)

(def ^{:tag 'long :const true} k 100)

(let [i k] (+ i 10)))

不会发出警告。:const经常被错误使用,但这是它的良好用途。

...