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 返回假,但 .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())
据我所知,原始类型提示只在局部绑定中有效。从变量类型提示中推断它们不受支持。我不确定这一点在哪里有文档记录,所以最好去挖掘一下。
by
找到了,已作为回答发布

2 个回答

+1
by

文档中只提到支持“局部上下文”使用原始类型提示。

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

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

+1
by

不管这里的类型提示是什么,vars始终是Object,示例用法中的值将是包装值(因此不是原始类型)。然而,如果标记为^:const并且是原始类型,编译器将在let中内联该vars,与原始long一起,在这种情况下它将确实是原始类型。

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

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

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

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

...