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

Reply via email to