【问题标题】:Reflection warning in code generated by Clojure macroClojure 宏生成的代码中的反射警告
【发布时间】:2015-06-07 06:27:56
【问题描述】:

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

(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表明生成的代码确实有typehint,如果我手动编写相同的代码而不使用宏,没有反射警告:

(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"

【问题讨论】:

  • 当我运行macroexpand-1 行时,我看不到类型提示。但是,我认为宏有问题;它不接受文字字符串:(my-macro "foo") ;=> java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IObj.
  • 您应该会看到类型提示。您确定将*print-meta* 设置为true?

标签: clojure macros


【解决方案1】:

标签应该是一个符号,而不是一个类。所以下面的代码有效:

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

这其实在documentation of special forms里有说明:

:标签

命名类或 Class 对象的符号,表示 Java var 中对象的类型,如果对象是 fn.

macroexpand-1 显示的类型提示无效但看起来与正确的提示完全相同,这一事实非常具有误导性:)

【讨论】:

  • 请注意,在String 上使用反引号更正确,而不是普通的引号。对于 java.lang 中的类也是一样的,因为它们是在所有命名空间中导入的,因此非限定引用有效,但如果您尝试使用像 ArrayList 这样的类,它只会在已导入 java.util.ArrayList 的命名空间中有效.
猜你喜欢
  • 1970-01-01
  • 2011-06-06
  • 1970-01-01
  • 2019-10-18
  • 2013-12-30
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 2014-05-19
相关资源
最近更新 更多