Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection warning in code generated by Clojure macro

Tags:

macros

clojure

I don't understand why the following code produces 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 does have the typehint, 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

Benchmarking the function shows that the warning is not just a red herring, the 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"
like image 285
noziar Avatar asked Oct 19 '22 10:10

noziar


1 Answers

The tag should be a Symbol, not a Class. So the following code works:

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

This is actually stated in the documentation of special forms:

:tag

a symbol naming a class or a Class object that indicates the Java type of the object in the var, or its return value if the object is a fn.

The fact that macroexpand-1 shows a type hint that is invalid but looks exactly like the correct one is quite misleading :)

like image 170
noziar Avatar answered Dec 22 '22 06:12

noziar