14.1 类型标识符 (Type Specifiers)

类型在 Common Lisp 里不是对象。举例来说,没有对象对应到 integer 这个类型。我们像是从 type-of 函数里所获得的,以及作为传给像是 typep 函数的参数,不是一个类型,而是一个类型标识符 (type specifier)。

一个类型标识符是一个类型的名称。最简单的类型标识符是像是 integer 的符号。这些符号形成了 Common Lisp 里的类型层级。在层级的最顶端是类型 t ── 所有的对象皆为类型 t 。而类型层级不是一棵树。从 nil 至顶端有两条路,举例来说:一条从 atom ,另一条从 listsequence

一个类型实际上只是一个对象集合。这意味着有多少类型就有多少个对象的集合:一个无穷大的数目。我们可以用原子的类型标识符 (atomic type specifiers)来表示某些集合:比如 integer 表示所有整数集合。但我们也可以建构一个复合类型标识符 (compound type specifiers)来参照到任何对象的集合。

举例来说,如果 ab 是两个类型标识符,则 (or a b) 表示分别由 ab 类型所表示的联集 (union)。也就是说,一个类型 (or a b) 的对象是类型 a 或 类型 b

如果 circular? 是一个对于 cdr 为环状的列表返回真的函数,则你可以使用适当的序列集合来表示: [1]

  1. (or vector (and list (not (satisfies circular?))))

某些原子的类型标识符也可以出现在复合类型标识符。要表示介于 1 至 100 的整数(包含),我们可以用:

  1. (integer 1 100)

这样的类型标识符用来表示一个有限的类型 (finite type)。

在一个复合类型标识符里,你可以通过在一个参数的位置使用 * 来留下某些未指定的信息。所以

  1. (simple-array fixnum (* *))

描述了指定给 fixnum 使用的二维简单数组 (simple array)集合,而

  1. (simple-array fixnum *)

描述了指定给 finxnum 使用的简单数组集合 (前者的超类型 「supertype」)。尾随的星号可以省略,所以上个例子可以写为:

  1. (simple-array fixnum)

若一个复合类型标识符没有传入参数,你可以使用一个原子。所以 simple-array 描述了所有简单数组的集合。

如果有某些复合类型标识符你想重复使用,你可以使用 deftype 定义一个缩写。这个宏与 defmacro 相似,但会展开成一个类型标识符,而不是一个表达式。通过表达

  1. (deftype proseq ()
  2. '(or vector (and list (not (satisfies circular?)))))

我们定义了 proseq 作为一个新的原子类型标识符:

  1. > (typep #(1 2) 'proseq)
  2. T

如果你定义一个接受参数的类型标识符,参数会被视为 Lisp 形式(即没有被求值),与 defmacro 一样。所以

  1. (deftype multiple-of (n)
  2. `(and integer (satisfies (lambda (x)
  3. (zerop (mod x ,n))))))

(译注: 注意上面代码是使用反引号 ` )

定义了 (multiple-of n) 当成所有 n 的倍数的标识符:

  1. > (type 12 '(multiple-of 4))
  2. T

类型标识符会被直译 (interpreted),因此很慢,所以通常你最好定义一个函数来处理这类的测试。