On Sun, Nov 21, 2010 at 12:21 PM, Shantanu Kumar
<[email protected]> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en