Clojure: Metadata

By Xah Lee. Date: . Last updated: .

This page is WORK IN PROGRESS.

This page is a tutorial of Clojure's Metadata.

Clojure's symbol, or any data structure (For example, list, vector, map, set, ….), can have “metadata” attached.

Clojure “metadata” is a Clojure map data type (that is: {…}).

Add Metadata

To attach metadata, use with-meta

(with-meta obj m) → Returns a object of the same type and value as obj, with map m as its metadata. clojure.core/with-meta

Note: it returns a new object.

;; define a variable
(def hh 3)

;; create new object gg with same value as hh, plus metadata
(def gg (with-meta 'hh {:bb 3}))

Get Metadata

To get metadata, use meta.

(meta obj) → Returns a map that is the metadata of obj, returns nil if there is no metadata. clojure.core/meta

;; define a variable
(def hh 3)

;; create new object gg with same value as hh, plus metadata
(def gg (with-meta 'hh {:bb 3}))

;; get the metadata of gg
(pr
 (meta gg)) ; prints {:bb 3}

“Change” Metadata by Creating New Object

Use vary-meta to create a new object with new metadata.

(vary-meta obj f) → Returns a object of the same type and value as obj, with (apply f (meta obj)) as its metadata. clojure.core/vary-meta clojure.core/apply

;; example of creating new object based on another object's metadata

;; define a variable
(def hh 3)

;; create new object gg with same value as hh, plus metadata
(def gg (with-meta 'hh {:bb 3}))

;; define yy, with yy being equal to gg, and with gg's metadata, but added the pair :t4 4
(def yy
     (vary-meta gg
                (fn [xx] (assoc xx :t4 4))))

;; print the metadata of yy
(pr (meta yy)) ; prints {:bb 3, :t4 4}

When using “vary-meta” you need to pass a function ƒ that add/remove/modify maps data type. For example: (vary-meta obj ƒ)

Your ƒ typically have this form (fn …), and it typically calls {assoc, dissoc, merge, …}, functions that works with map. clojure.core/assoc clojure.core/dissoc clojure.core/merge

For syntax convenience, “vary-meta” takes a second argument.

(vary-meta obj f args) → Returns a object of the same type and value as obj, with (apply f (meta obj) args) as its metadata.

Here's a example, doing the same thing as above example, showing how this can shorten syntax.

;; define a variable
(def hh 3)

;; create new object gg with same value as hh, plus metadata
(def gg (with-meta 'hh {:bb 3}))

;; define yy, with yy being equal to gg, and with gg's metadata added the pair :t4 4
(def yy
     (vary-meta gg
                assoc
                :t4 4)
     )

;; print the metadata of yy
(pr (meta yy)) ; prints {:bb 3, :t4 4}

Important: a object's metadata never change. You always create new object with new metadata.

Metadata Syntax Shortcuts

^{…} obj is same as (with-meta obj {…})

^:x obj is same as (with-meta obj {:x true})

^"…" obj is same as (with-meta obj {:tag "…"})

^String obj is same as (with-meta obj {:tag java.lang.String}) (this helps the Clojure compiler)

Metadata Syntax Shortcut for Literal Forms

The syntax #^map form will attach metadata map to the form form.

;; syntax shortcut for attaching metadata to a vector form
(pr
 (meta #^{:tt 7} [3 4])
 ) ; prints {:tt 7}

Warning: #^{…} y) is similar to (with-meta y {…})) but is not the same.

(pr
 (meta #^{:tt 7} (list 3 4))
 ) ; prints nil

;; because #^{:tt 7} attaches metadata to the literal form 「(list 3 4)」, but that form returns 「(3 4)」, the 「(3 4)」 doesn't have metadata attached to it
;; something like that. nobody can really explain this

metadata are ignored in comparison

metadata is not considered part of the object. So, 2 object that only differ in metadata will still return true when compared by (= …).

clojure.core/=

clojure.core/identical? (test if 2 objects are the same (in memory))

however, a object with different metadata is a different object.

clojure.core/var

warning about Clojure metadata

Warning: function that works on collection or sequence may or may not preserve metadata. You should check if you rely on metadata.

Show Metadata in REPL

In repl, you can set it to show metadata. Type

(set! *print-meta* true)

clojure.core/set!

Reference