Clojure: Metadata
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)
-
Return 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)
-
Return 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)
-
Return 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 f that
add/remove/modify maps data type.
e.g. (vary-meta obj f)
Your f 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)
-
Return 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 ;; #^{: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
- Clojure metadata is not the normal sense of metadata associated with files etc such as timestamp, author, etc.
- Do not use metadata to store your program logic or data.
- Clojure metadata is NOT like JavaScript object properties, NOR lisp symbol properties.
- Clojure metadata is kinda experimental.
- metadata is used by compiler, or, as info for the programer.
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!