由Clojure宏生成的代码中的反射警告



我不明白为什么下面的代码会产生一个反射警告:

(set! *warn-on-reflection* true)
(defmacro my-macro [k] `(.length ~(with-meta k {:tag String})))
(defn my-fun1 [k] (my-macro k))
;; Reflection warning, /tmp/form-init2370243866132870536.clj:1:18 - reference to field length can't be resolved.

使用macroexpand-1显示生成的代码确实有类型提示,如果我手动编写相同的代码而不使用宏,则没有反射警告:

(set! *print-meta* true)
(macroexpand-1 '(my-macro k))
;; (.length ^java.lang.String k)
(defn my-fun2 [k] (.length ^String k))
;; All good, no reflection warning

对函数进行基准测试表明,警告不仅仅是转移注意力,反射实际上发生在运行时:

(time (reduce + (map my-fun1 (repeat 1000000 "test"))))
;; "Elapsed time: 3080.252792 msecs"
(time (reduce + (map my-fun2 (repeat 1000000 "test"))))
;; "Elapsed time: 275.204877 msecs"

标签应该是Symbol,而不是class。所以下面的代码可以工作:

(defmacro my-macro [k] `(.length ~(with-meta k {:tag `String})))

这实际上在特殊形式的文档中有说明:

:标签

一个命名类或类对象的符号,表示Javavar中对象的类型,如果对象是a,则为其返回值fn。

事实上,macroexpand-1显示的类型提示是无效的,但看起来完全像正确的是相当误导:)

最新更新