Clojure

Metadata

Symbols and collections support metadata, a map of data about the symbol or collection. The metadata system allows for arbitrary annotation of data. It is used to convey information to the compiler about types, but can also be used by application developers for many purposes, annotating data sources, policy etc.

An important thing to understand about metadata is that it is not considered to be part of the value of an object. As such, metadata does not impact equality (or hash codes). Two objects that differ only in metadata are equal.

That said, metadata and its relationship to an object is immutable - an object with different metadata is a different object. One consequence of this is that applying metadata to a lazy sequence will realize the head of the sequence so that both objects can share the same sequence.


(meta obj)

Returns the metadata of obj, returns nil if there is no metadata.

(pprint (meta #'+)) ;; #'+ is the + var

;; {:added "1.2",
;;  :name +,
;;  :file "clojure/core.clj",
;;  :column 1,
;;  :line 984,
;;  :arglists ([] [x] [x y] [x y & more]),
;;  ...

(with-meta obj map)

Returns an object of the same type and value as obj, with map as its metadata.

(def m ^:hi [1 2 3])
(meta (with-meta m {:bye true}))
;; {:bye true}

*print-meta*

If set to logical true, when printing an object, its metadata will also be printed in a form that can be read back by the reader.

(def m ^:hi [1 2 3])
(binding [*print-meta* true]
  (prn m))

;; ^{:hi true} [1 2 3]

(vary-meta obj map)

Returns an object of the same type and value as obj, with (apply f (meta obj) f & args) as its metadata.

(def m ^:hi [1 2 3])
(meta (vary-meta m merge {:bye true}))
;; {:hi true, :bye true}

(alter-meta! ref f & args) and (reset-meta! ref map)

Modify or reset the metadata respectively for a namespace/var/ref/agent/atom.

Metadata Reader Macros

In addition to with-meta, there are a number of reader macros (The Reader: Macro Characters) for applying metadata to the expression following it at read-time:

  • ^{:doc "How it works!"} - adds the metadata map to the metadata of the next value read

  • ^:dynamic - like ^{:dynamic true}

  • ^String - like ^{:tag java.lang.String}

  • ^"java.lang.String" - like ^{:tag java.lang.String}

The :tag key is used to hint an objects type to the Clojure compiler. See Java Interop: Type Hints for more information and a complete list of special type hints.

It is possible to add multiple pieces of metadata by chaining the metadata reader macros together. For example: ^:dynamic ^ints obj would apply both the :dynamic flag and ints type-hint to obj. Metadata chains from right to left (left takes precedence).

Note that metadata reader macros are applied at read-time, not at evaluation-time, and can only be used with values that support metadata, like symbols, vars, collections, sequences, namespaces, refs, atoms, agents, etc. Some important exceptions that don’t support metadata are strings, numbers, booleans, Java objects, keywords (these are cached and can be shared within the runtime), and deftypes (unless they explicitly implement clojure.lang.IMeta).