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,它会导致编译器认为它不是,并且类型无法推断。

我不太确定编译器中的确切位置,但我找到了以下内容: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1341

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

 if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
据我所知,原始类型注解只在局部绑定中有效。从变量类型注解中推断它们是不支持的。我不确定它在哪里有文档说明,所以仍然是个好问题。
by
找到了,已将其作为答案发布

2 答案

+1
by

文档说明只有“局部上下文”支持基本类型提示

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

对于基本类型提示,变量(返回类型)的标签不受支持。

+1
by

无论这里的类型提示是什么,vars 总是 Object,在示例用法中将是 boxed value(所以不是原始的)。然而,如果标记为 ^:const 且为原始的,编译器将在 let 中内联 var 和原始 long,在这种情况下它将会是一个原始值。

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

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

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

不会警告。:const 常常被误用,但这是它的良好用法。

...