Hello,
I am implementing a pretty printer and while adding/testing support for *print-meta* I noticed that pr prints an object whose metadata has metadata in the following way:
user> (binding [*print-meta* true]
(pr-str (with-meta 'foo (with-meta {:bar :baz} {:frob :zork}))))
"^^{:frob :zork} {:bar :baz} foo"
When reading such a form, only the "outermost" metadata is preserved:
user> (binding [*print-meta* true]
(let [s (pr-str (with-meta 'foo (with-meta {:bar :baz} {:frob :zork})))
o (clojure.edn/read-string s)]
{:o o
:meta (meta o)
:metameta (meta (meta o))}))
{:o foo, :meta {:bar :baz}, :metameta nil}
The behaviour is the same for clojure.core/read-string and also the tools.reader readers.
Can anyone clarify for me if this is either:
- simply GIGO
- a bug in the printer
- a bug in the readers
- a state that should be somehow disallowed
- something else?
Neither the metadata reference nor the reader reference explicitly allow or disallow such a thing, as far as I can tell. Should they?
N.B. this is not an issue in any kind of production context and I'm not aware of any library or program that constructs such a thing. It's simply something I tested to see if/how it would break the pretty printer I'm building.
EDIT:
I dug into this some more with Nicola. Here's a perhaps much clearer case that demonstrates the reader isn't propagating nested metadata:
user> (let [o ^^:foo {1 2} {3 4}]
{:o o :meta (meta o) :metameta (meta (meta o))})
{:o {3 4}, :meta {1 2}, :metameta nil}
:metameta should be {:foo true}.
In this section of MetaReader.invoke we are iterating over the entries of the meta map and manually associng them into the existing meta of the object; any metadata on the meta map itself is not preserved. I have a patch that adds a failing test case and correctly propagates the the meta map's metadata. If this is worth a ticket then I'm more than happy to attach it to that or send it to you directly.