Clojure 2024 状态调查!中分享您的想法。

欢迎!请访问关于页面以获取更多关于如何使用本站的信息。

+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())
据我所知,原始类型注解仅在局部绑定中有效。不支持从变量类型注解中推断它们。我不太确定这在哪有文档,但仍然值得查找。
找到了,已作为答案发布

2 个答案

+1 投票

文档中说明,只有“局部上下文”支持原始类型提示。

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

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

+1 投票

无论此处是什么类型提示,vars始终是Object,示例用法中将是一个包装值(而非原始值)。但是,如果标记为^:const和原始类型,编译器将内联var在let中,使用原始long,在这种情况下它将是原始值。

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

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

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

不会发出警告。:const通常使用不正确,但这是它的一个好用法。

...