Clojure Macro Reflection Warning

I do not understand why the following code triggers a reflection warning:

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

Using macroexpand-1 shows that the generated code is of type hint, and if I write the same code manually without using a macro, there is no reflection warning:

 (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 

Function benchmarking shows that a warning is not just a red herring; reflection actually occurs at runtime:

 (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" 
+5
source share
1 answer

The tag should be a symbol, not a class. Thus, the following code works:

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

This is actually indicated in the documentation of special forms :

Tag

character denoting a class or class object that indicates the Java type of the object in var or its return value if the object is n.

The fact that macroexpand-1 shows the wrong hint type but looks exactly the same as the correct one is pretty misleading :)

+3
source

All Articles