Question

Clojure meta is only preserved if a function takes care to do so and the Clojure core functions do not globally preserve meta. The general rule of thumb I've heard is that collection functions like conj, assoc, etc are supposed to preserve meta but sequence functions like map, filter, take, etc do not preserve meta.

Is there a list somewhere of what functions DO preserve meta?

Was it helpful?

Solution

It's all about the types. Sequence functions act like they call seq on their argument and thus doesn't always return the same type of object. Collection functions and type-specific functions doesn't call seq and return an object of the same type as what was given to them. It kind of make them give the illusion of returning the same object (this might be the reasoning for this behavior) even if that's really not the case. We could say that the rule of thumb is that a functions preserve the meta when it preserve the type.

user> (meta (seq (with-meta (list 1) {:a 1})))
{:a 1}
user> (meta (seq (with-meta (vector 1) {:a 1})))
nil

Be sure to be aware of when laziness is involved tough:

user> (type (list 1))
clojure.lang.PersistentList
user> (type (map identity (list 1)))
clojure.lang.LazySeq
user> (meta (seq (with-meta (map identity (list 1)) {:a 1})))
nil

For a list of functions that preserves meta on collection, see the data structures page. The ones not preserving meta are under the sequences page, with the exception of when they return an object of the same type.

Under the hood I'm not quite sure about the details since laziness and chunked sequence has been added, but you can look at the cons, seq and seqFrom methods from the RT class. The functions not preserving meta-data go through these methods. While the collection functions end up using methods specific to their types.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top