On Wed, Jan 25, 2012 at 11:58 AM, Ben Mabey <b...@benmabey.com> wrote:
> 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?

It's even stranger than that. It won't work with local Vars:

user=> (with-local-vars [a (fn [^double x] (+ x 0.5))] (.setMeta a
{:tag Double/TYPE}) (pt (a 0.1)))
:object

And yes, there's a {:tag double} on a after the .setMeta call. In
fact, that degree of hackery is needed. Neither ^double nor ^{:tag
Double/TYPE} on the a in the binding form will attach the metadata to
the Var, unlike with def, where the latter works (and the former seems
to, at first, but fails when the fn is called with a *very strange*
error message about a class named clojure.core$double@1eb1db2 not
being resolved).

Attaching ^double or ^{:tag Double/TYPE} to the (fn ...) form itself
also does not work, there or with a normal let-bound local.

Either PrimitiveTester doesn't work the way it seems like it should,
or else something's strange/wonky with how Clojure compiles function
calls that don't use a namespaced global Var.

Actually, on further testing, I'm not sure that (def ^{Double/TYPE}
...) is really working either, rather than boxing and then unboxing on
each call:

user=> (def ^{:tag Double/TYPE} b (fn [^double x] (+ x 0.5)))
#'user/b
user=> (defn c ^double [^double x] (+ x 0.5))
#'user/c
user=> (pt (b 0.1))
:double
user=> (pt (c 0.1))
:double

So far, so good? But:

user=> (ancestors (.getClass b))
#{java.util.concurrent.Callable clojure.lang.AFn clojure.lang.IObj
  clojure.lang.IFn clojure.lang.IFn$DO clojure.lang.IMeta
  java.lang.Object java.io.Serializable java.lang.Runnable
  java.util.Comparator clojure.lang.AFunction clojure.lang.Fn}

IFn$DO?

user=> (ancestors (.getClass c))
#{java.util.concurrent.Callable clojure.lang.AFn clojure.lang.IObj
  clojure.lang.IFn$DD clojure.lang.IFn clojure.lang.IMeta
  java.lang.Object java.io.Serializable java.lang.Runnable
  java.util.Comparator clojure.lang.AFunction clojure.lang.Fn}

IFn$DD?

user=> (def d (fn ^double [^double x] (+ x 0.5)))
#'user/d
user=> (ancestors (.getClass d))
#{java.util.concurrent.Callable clojure.lang.AFn clojure.lang.IObj
  clojure.lang.IFn$DD clojure.lang.IFn clojure.lang.IMeta
  java.lang.Object java.io.Serializable java.lang.Runnable
  java.util.Comparator clojure.lang.AFunction clojure.lang.Fn}

IFn$DD again here.

This suggests:

user=> (let [a ^clojure.lang.IFn$DD (fn ^double [^double x] (+ x
0.5))] (pt (a 0.1)))
:object

Nope, that doesn't work either!

This really *should* be simple. Therefore, there's a bug somewhere.
But I'm not sure where. Compiler.java, most likely.

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