On 1/25/12 2:25 AM, Tassilo Horn wrote:
Hi again,

I think, I got it.  I wrote a little helper function to print the
metadata of a form:

--8<---------------cut here---------------start------------->8---
(use 'clojure.walk)
(defn print-meta
   ([form level]
      (prewalk
       (fn [x]
         (when-let [m (meta x)]
           (println "Level" level ":" x "=>" m)
           (print-meta m (inc level)))
         x)
       form))
   ([form]
      (print-meta form 0)))
--8<---------------cut here---------------end--------------->8---

Now see what happens:

--8<---------------cut here---------------start------------->8---
user>  (print-meta (macroexpand-all '(def b (fn ^foo [^bar x] ^baz x))))
Level 0 : x =>  {:tag bar}
Level 0 : x =>  {:tag baz}
(def b (fn* ([x] x)))
user>  (print-meta (macroexpand-all '(defn b ^foo [^bar x] ^baz x)))
Level 0 : b =>  {:arglists (quote ([x]))}
Level 1 : [x] =>  {:tag foo}
Level 1 : x =>  {:tag bar}
Level 0 : x =>  {:tag bar}
Level 0 : x =>  {:tag baz}
(def b (fn* ([x] x)))
--8<---------------cut here---------------end--------------->8---

So the return type metadata is actually added to the :arglist metadata
contents, so it's actually meta-metadata.  What you seem to have to do
is to make sure the :arglists metadata is there and reflects the type
hints on the actual function:

--8<---------------cut here---------------start------------->8---
user>  (def ^{:arglists '(^double [^double x])}
           with-def  (fn ^double [^double x] (+ x  0.5)))
#'user/with-def
user>  (pt (with-def 1))
:double
--8<---------------cut here---------------end--------------->8---

Well, that's not really obvious.  Maybe `def' could do a better job here
and build up a correct :arglists metadata value somehow...

Bye,
Tassilo

Hi Tassilo,
Thanks for doing the detective work! I still have some questions though... In my example I used def because I was able to reproduce the bug with def and it was the easiest way to show it. However, in practice these anonymous functions won't be bound to a root var (hence the anonymous part :) ). I would like to be able to write code like this:

(map-doubles (fn ^double [^double x] (+ x 0.5)) array)

I don't seem to be able to do that since the type hints are not put into effect without a var (it would seem):

(pt ((fn ^double [^double x] (+ x 0.5)) 1.0)) ; => :object

Not just put into effect, but not even stored on the function (as far as I can tell):

(meta  (fn ^double [^double x] (+ x 0.5))) ; => nil

I've tried adding the appropriate metadata onto the function (I'm stealing the metadata from our earlier with-defn):

(meta (with-meta (fn ^double [^double x] (+ x 0.5)) (select-keys (meta #'with-defn) [:arglists]))) ; => {:arglists ([x])}

I can verify that the arglist is tagged with the primitive just like it is with the var #'with-defn:

(defn arglist-tag [var]
  (-> var meta :arglists first meta :tag))

(arglist-tag #'with-defn) ; => double
(arglist-tag (with-meta (fn ^double [^double x] (+ x 0.5)) (select-keys (meta #'with-defn) [:arglists])))A ; => double

Sadly, even with the arglist meta-metadata boxing is still occurring:

(pt ((with-meta (fn ^double [^double x] (+ x 0.5)) (select-keys (meta #'with-defn) [:arglists])) 1.0)) ; => object

It is probably obvious that this would not work to someone who understands clojure's implementation, but to me this was a surprise. From this I gather that type hints on vars are what clojure uses and type hints on the actual functions are ignored.

Am I missing something, or does the new primitive support only apply to bound functions?

-Ben

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