metadata and defn: why meta returns a non nil value on a function value ?

7 views
Skip to first unread message

Ludovic Kuty

unread,
Feb 7, 2010, 10:40:26 AM2/7/10
to Clojure
Hello everyone,

I came upon a weird behaviour of Clojure. Weird as far as I understand
what is going on with metadata. Maybe not very far.

I understand that the reader macro #^ associates metadata with a form,
for use by macros or the compiler as illustrated below.

user=> (defmacro m [form] `(println (meta (quote ~form))))
#'user/m
user=> (m #^{:foo "bar"} '(1 2 3))
{:line 7, :foo bar}
nil
user=> (m #^{:foo "bar"} x)
{:foo bar}
nil

I think using a macro like m is the only way to get the metadata
associated with the symbol because symbols are different everytime we
construct one, even if their spelling is the same. That explains why
(defn ff [#^Integer x] (println (meta 'x)) x) fails to display the
metadata associated with formal argument x.

But when I define a function with defn augmenting the definition with
metadata, I observe two things I don't understand. I will comment on
the code below to illustrate my problem.

mac:clojure ludo$ clj
Clojure 1.2.0-master-SNAPSHOT
user=> (defn #^{:doc "doubles argument" :tag Integer} f [#^Integer x]
(* x 2))
#'user/f
user=> (meta f)
{:ns #<Namespace user>, :name f}
user=> (defn #^{:doc "doubles argument" :tag Integer} f [#^Integer x]
(* x 2))
#'user/f
user=> (meta f)
{:ns #<Namespace user>, :name f, :file "NO_SOURCE_PATH", :line
1, :arglists ([x]), :doc "doubles argument", :tag java.lang.Integer}
user=> (meta #'f)
{:ns #<Namespace user>, :name f, :file "NO_SOURCE_PATH", :line
3, :arglists ([x]), :doc "doubles argument", :tag java.lang.Integer}

The first defn defines a fuction called f with metadata provided by
the reader for the symbol f and the symbol x. Thus the special form
def (hidden behind the macro defn) takes the metadata and associates
it with the var #'f.

1) When I call (meta f), I get something. I thought there can't be any
metadata on a function value. But I see :ns and :name in the map.

2) If a redefine f with the same call to defn, I get full metadata on
the value of f as well as #'f. As meta is an ordinary function, the
argument f gets evaluated, producing a function value and then I
retrieve the metadata associated with the value and get the whole
stuff as if I called it on #'f.

Point 1 is weird but point 2 is even weirder.

I would be thankful if anyone could provide insights on this problem.

Ludovic Kuty

Michał Marczyk

unread,
Feb 7, 2010, 10:59:44 AM2/7/10
to clo...@googlegroups.com
My conjecture would be that the Var gets constructed and assigned its
name in the current namespace first, then the function is constructed,
then metadata gets attached to the Var. Thus the first defn pulls
metadata to be attached to the newly constructed function from a newly
constructed Var, whereas the second defn re-uses the Var which was in
place earlier...

This would appear to support the above:

user> (def #^{:foo "bar"} x 5)
#'user/x
user> (meta #'x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1, :foo "bar"}
user> (defn x [] 5)
#'user/x
user> (x)
5
user> (meta x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1, :foo "bar"}

Sincerely,
Michał

Michał Marczyk

unread,
Feb 7, 2010, 11:03:14 AM2/7/10
to clo...@googlegroups.com
Oh, and of course the final line of the above REPL interaction is this:

user> (meta #'x)
{:ns #<Namespace user>, :name x, :file "NO_SOURCE_FILE", :line 1,

:arglists ([])}

Thus there's a sort of an off-by-one error in that the function
created by defn gets the metadata which was attached to its Var
*before* the defn form was evaluated (which will be the metadata of a
newborn Var if the defn form is the first one to have it constructed),
whereas the Var is subsequently given the correct metadata for the
function. Ouch. :-)

Sincerely,
Michał

Michał Marczyk

unread,
Feb 7, 2010, 8:54:52 PM2/7/10
to clo...@googlegroups.com
On 7 February 2010 16:40, Ludovic Kuty <ludovi...@gmail.com> wrote:
> 1) When I call (meta f), I get something. I thought there can't be any
> metadata on a function value. But I see :ns and :name in the map.

I completely forgot about the first point... Metadata on functions has
been introduced some time ago, can't seem to remember when exactly.

Also, I've researched the second issue a little bit more and will post
my findings in a second while changing the title of this thread in
hope of bringing extra attention to what I think may be a bug in
Compiler.java...

Sincerely,
Michał

Ludovic Kuty

unread,
Feb 8, 2010, 1:20:11 AM2/8/10
to Clojure
Thanks for having shared your findings.

I saw a post where Rich Hickey mentionned that he was planning to add
metadata to functions but I didn't know it was already done from what
I saw on the net.

On Feb 8, 2:54 am, Michał Marczyk <michal.marc...@gmail.com> wrote:

Reply all
Reply to author
Forward
0 new messages