Metadata loss. What am I doing wrong?

195 views
Skip to first unread message

Andrey Antukh

unread,
May 5, 2015, 2:31:40 PM5/5/15
to clo...@googlegroups.com
Hi!

I have some trouble with clojure metadata / reader and I do not know if I'm doing something wrong.

I have this code:

(defn some-func [])

(def func ^:abc some-func)

(assert (= (meta func) {:abc true}))

(def data [[:bar (with-meta some-func {:abc true})]
           [:baz ^:abc some-func]])

(assert (= (meta (get-in data [0 1])) {:abc true}))
(assert (= (meta (get-in data [1 1])) {:abc true}))

It fails in the first assert and in the last (if I comment the first one obviously). I do not understand why that form of metadata does not works

Thank you very much.

Regards.
Andrey

--

James Reeves

unread,
May 5, 2015, 2:45:21 PM5/5/15
to clo...@googlegroups.com
When dealing with metadata, it's important to understand the difference between these two expressions:

    ^{:foo :bar} baz

    (with-meta baz {:foo :bar})

The first expression attaches metadata to the 'baz' symbol at compile time. The second expression attaches metadata to the data held in 'baz' at runtime.

The ^ syntax is essentially used for passing information to the Clojure compiler, and to macros. It's not runtime information.

- James

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andy-

unread,
May 5, 2015, 3:49:07 PM5/5/15
to clo...@googlegroups.com, ni...@niwi.be
In addition to James comment: IMO clojure.org/metadata should be clearer about this. It's mentioned more clearly on the reader page:
"The metadata reader macro first reads the metadata and attaches it to the next form read (see with-meta to attach meta to an object):"

Stress on *next form read*.

Andrey Antukh

unread,
May 5, 2015, 5:01:19 PM5/5/15
to Andy-, clo...@googlegroups.com, Andrei Antoukh
Thanks to both for the responses, but I stil not clearly understand.

The documentation says very clearly that:

In addition to with-meta, there are a number of reader macros (The Reader: Macro Characters) for applying metadata to the expression following it:
^{:doc "How obj works!"} obj - Sets the metadata of obj to the provided map.
Equivalent to (with-meta obj {:doc "How obj works!"})

(def foo ^:abc [1 2 3]) -> (meta foo) -> {:abc true}
(def foo ^:abc some-func) -> (meta foo) -> nil
(def foo ^:abc 'some-symbol) -> (meta foo) -> nil (In clojure programming book uses example attaching metadata using the reader to the symbol, but seems it not works as expected)

Is a little bit confusing. The metadata documentation says clearly that are equivalent, but are not equivalent.


Thank you very much again.

Regards.
Andrey

James Reeves

unread,
May 5, 2015, 5:26:25 PM5/5/15
to clo...@googlegroups.com, Andy-, Andrei Antoukh
The documentation is rather misleading, as it implies that "obj" can be a symbol. However, because ^ is a reader macro, it is applied to "obj" before it is evaluated.

Clojure maps, vectors and sets all evaluate to themselves, so attaching metadata to the unevaluated expression via the ^ reader macro, is the same as attaching it to the evaluated expression via with-meta.

So:

    ^:abc foo
    ^:abc [1 2 3]

Will both attach the metadata directly to the literal value following it, but because 'foo' evaluates to something else, the reader metadata is lost after the symbol is evaluated.

- James

--

Andy-

unread,
May 5, 2015, 5:43:19 PM5/5/15
to clo...@googlegroups.com, ni...@niwi.be, andre...@gmail.com
Frankly, I would've (meta ^:abc 'some-symbol) expected to work. Maybe somebody else can weigh in on why this one is a no-go.

James Reeves

unread,
May 5, 2015, 6:04:32 PM5/5/15
to clo...@googlegroups.com, ni...@niwi.be, andre...@gmail.com
I expect because 'some-symbol is shorthand for (quote some-symbol), so you're attaching the metadata to a list that disappears once it's evaluated.

- James

--

adrian...@mail.yu.edu

unread,
May 5, 2015, 6:04:50 PM5/5/15
to clo...@googlegroups.com, ni...@niwi.be, andre...@gmail.com
Because ' is a reader macro which expands to the list (quote some-symbol), so the metadata is applied to the list, and not the symbol. You can verify this in the REPL - (meta (quote ^:abc 'some-symbol))

wpar...@yahoo.com

unread,
May 5, 2015, 8:17:07 PM5/5/15
to clo...@googlegroups.com, ni...@niwi.be
From your comments, I suspect this may be a source of confusion as well:

When you have something like (defn ^{:doc "Increments"} a-fn [x] (+ x 1)) the metadata is attached to the symbol at read time.  However, during the compilation process, the metadata on the symbol is transferred to the Var that holds the function created.  If you want to access this metadata you have to access the Var itself, not the function.

user=> (meta a-fn)
nil
user=> (meta #'a-fn)
{:ns #<Namespace user>, :name a-fn, :file "NO_SOURCE_PATH", :column 1, :line 1, :arglists ([x]), :doc "Increments"}

The #' syntax is shorthand for accessing the Var.  You can see this process in the following code:

(let [fn-def-sym (with-meta 'b-fn {:here true})
      fn-def-code `(defn ~fn-def-sym [x#] (+ x# 1))]
  (eval fn-def-code))

#'user/b-fn
user=> (b-fn 2)
3
user=> (meta b-fn)
nil
user=> (meta #'b-fn)
{:ns #<Namespace user>, :name b-fn, :file "NO_SOURCE_PATH", :here true, :column 1, :line 1, :arglists ([x__769__auto__])}

The important thing here is that the definition code is manually built up using a symbol that has metadata given manually, which is then transferred to a Var when the code is evaluated.

Mike Rodriguez

unread,
May 5, 2015, 8:51:05 PM5/5/15
to clo...@googlegroups.com

What you wanted here was

(meta '^:abc some-symbol)

It's a little weird but the reader attaches the metadata to the symbol. Then the quote just evaluates directly to the same symbol, so the metadata is preserved.

I agree that metadata can be confusing though. Especially around where AND HOW you put the metadata for a functions return value(s).

See [1] for more on that confusing track...

Also there are plenty of subtle "gotchas" to macros etc that do not preserve metadata. 'clojure.core/or' is a good example of that.

A search for "Clojure preserve metadata" will likely turn up a good list of Jiras out there to fix areas that do not preserve metadata as expected.

Looks like there are some tips at [2].

[1] http://dev.clojure.org/jira/browse/CLJ-1232
[2] http://stackoverflow.com/questions/4673011/what-functions-in-clojure-core-preserve-meta

Andy Fingerhut

unread,
May 5, 2015, 9:23:34 PM5/5/15
to clo...@googlegroups.com
The Eastwood [1] Clojure lint tool has a few warnings in it that warn about unused metadata in your code.

The :unused-meta-on-macro warns about metadata on macro invocations, which is usually ignored by Clojure [2].

The :wrong-tag warns about unused type tag metadata on Vars, and non-fully-qualified Java class names on arg vectors [3].

Andy


Mike Rodriguez

unread,
May 5, 2015, 9:36:23 PM5/5/15
to clo...@googlegroups.com
+1 to Eastwood. It is great.

Mike Rodriguez

unread,
May 5, 2015, 9:41:15 PM5/5/15
to clo...@googlegroups.com
In reference to [1]:

I do feel like the metadata loss on many macros is undesirable though and I wish it were addressed. It certainly feels "unhygienic", just in a new sense of the term.

[1] https://github.com/jonase/eastwood#unused-meta-on-macro

Andrey Antukh

unread,
May 6, 2015, 2:50:09 AM5/6/15
to clo...@googlegroups.com
Thank you very much to all!

Now I completely understand the metadata behavior with the reader. I'll try to adopt eastwood, thanks for the suggestion.

Is clearly that the documentation confuses a little bit. 

Cheers!
Andrey
Reply all
Reply to author
Forward
0 new messages