On Sun, Nov 21, 2010 at 12:21 PM, Shantanu Kumar <kumar.shant...@gmail.com> wrote: > Hi, > > Not sure if it makes sense, but I thought I'd ask anyway. Why are type > hints in a macro not passed to a body of code executed inside? > > user=> (set! *warn-on-reflection* true) > true > user=> (def s "Hello world") > #'user/s > user=> (.length s) > Reflection warning, NO_SOURCE_PATH:28 - reference to field length > can't be resolved. > 11 > user=> (defmacro in-macro [[x y] & body] `(let [^String ~x ~y]
Type hints using ^ are reader macros. Which means it applies the type hint to (unquote-splicing x) in the macro form, where it gets ignored, and not to the symbol passed as the first macro argument. user=> (macroexpand-1 '(in-macro [x "foo"] (.length x))) (clojure.core/let [x "foo"] (.length x)) user=> (second (macroexpand-1 '(in-macro [x "foo"] (.length x)))) [x "foo"] user=> (first (second (macroexpand-1 '(in-macro [x "foo"] (.length x))))) x user=> (meta (first (second (macroexpand-1 '(in-macro [x "foo"] (.length x)))))) nil This results in slow reflection: user=> (defn im [s] (in-macro [x s] (.length x))) #'user/im user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 1063.201 msecs" 6 user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 1064.87264 msecs" 6 user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 1068.73716 msecs" 6 What actually happens when you type-hint something? user=> (meta '^String x) {:tag String} user=> (.getClass (:tag (meta '^String x))) clojure.lang.Symbol It just puts a map {:tag 'String} as the symbol metadata, which we can do without a reader macro using with-meta: (defmacro in-macro [[x y] & body] `(let [~(with-meta x {:tag 'String}) ~y] ~...@body)) This fixes it; it tags the symbol passed as the first macro argument where the reader macro tagged the '(unquote-splicing x) in the macro's own code instead. user=> (macroexpand-1 '(in-macro [x "foo"] (.length x))) (clojure.core/let [x "foo"] (.length x)) user=> (second (macroexpand-1 '(in-macro [x "foo"] (.length x)))) [x "foo"] user=> (first (second (macroexpand-1 '(in-macro [x "foo"] (.length x))))) x user=> (meta (first (second (macroexpand-1 '(in-macro [x "foo"] (.length x)))))) {:tag String} This speeds things up by a *whole order of magnitude*: user=> (defn im [s] (in-macro [x s] (.length x))) #'user/im user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 166.06828 msecs" 6 user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 122.53536 msecs" 6 user=> (time (last (doall (take 100000 (map (comp im str) (iterate inc 1)))))) "Elapsed time: 99.33132 msecs" 6 Look ma, no reflection! -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en