Clojure

元数据

符号和集合支持元数据,一个关于符号或集合的数据映射。元数据系统允许对数据进行任意注释。它用于向编译器传达有关类型的信息,但也可以被应用程序开发人员用于多种目的,例如注释数据源、策略等。

关于元数据需要理解的一点是,它不被认为是对象值的一部分。因此,元数据不影响相等性(或哈希码)。只有元数据不同的两个对象是相等的。

也就是说,元数据及其与对象的关系是不可变的 - 具有不同元数据的对象是不同的对象。这样做的一个后果是,将元数据应用于延迟序列将实现序列的头部,以便两个对象可以共享同一个序列。


(meta obj)

返回 obj 的元数据,如果没有元数据则返回 nil。

(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)

返回一个与 obj 类型和值相同的对象,其元数据为 map。

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

*print-meta*

如果设置为逻辑真值,则在打印对象时,其元数据也将以读取器可以读取回的形式打印。

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

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

(vary-meta obj f & args)

返回一个与 obj 类型和值相同的对象,其元数据为 (apply f (meta obj) args)

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

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

分别修改或重置命名空间/var/ref/agent/atom 的元数据。

元数据读取器宏

除了 with-meta 之外,还有许多读取器宏(读取器:宏字符)用于在读取时将元数据应用于其后面的表达式

  • ^{:doc "How it works!"} - 将元数据映射添加到下一个读取的值的元数据中

  • ^:dynamic - 等同于 ^{:dynamic true}

  • ^String - 等同于 ^{:tag java.lang.String}

  • ^"java.lang.String" - 等同于 ^{:tag java.lang.String}

:tag 键用于提示 Clojure 编译器对象的类型。有关更多信息和特殊类型提示的完整列表,请参阅Java 交互:类型提示

可以通过将元数据读取器宏链接在一起添加多个元数据片段。例如:^:dynamic ^ints obj 将同时将 :dynamic 标志和 ints 类型提示应用于 obj。元数据从右到左链接(左侧优先)。

请注意,元数据读取器宏是在读取时应用的,而不是在求值时应用的,并且只能与支持元数据的 value 一起使用,例如符号、var、集合、序列、命名空间、ref、atom、agent 等。一些重要的例外情况**不支持**元数据,例如字符串、数字、布尔值、Java 对象、关键字(这些对象被缓存并在运行时内共享)以及 deftype(除非它们显式地实现了 clojure.lang.IMeta)。