4. 类型和类
4.1 引言
一个类型(type)是对象的一个(可能是无数)集合. 一个对象可以属于超过一个类型. 类型从不显式地被 Common Lisp 表示为对象. 相反, 它们是通过使用类型指定符(type specifier)间接引用的, 类型指定符是表示类型的对象.
新的类型可以使用 deftype, defstruct, defclass, 和 define-condition 来定义.
函数 typep 作为一个集合从属关系测试, 被用于确定一个给定的对象(object) 是否为给定的类型(type). 函数 subtypep 作为一个子集测试, 被用于确定一个给定的类型是否为另一个给定类型的子类型(subtype). 函数 type-of 返回一个给定对象所属的具体类型, 即便这个对象也属于一个或多个其他类型. (比如, 每一个对象都是类型 t, 但是 type-of 总是返回一个比 t 更具体的类型的类型指定符.)
对象, 而不是变量, 拥有类型. 通常, 任何变量可以有任何对象作为它的值. 可以声明一个变量只能获取通过显式类型声明指定的类型的值. 类型被安排在一个有方向的非循环图中, 除了存在相等的情况.
可以通过 declare, proclaim, declaim, 或 the 来对类型做出声明. 关于声明的更多信息, 见章节 3.3 (Declarations).
对象系统的基本对象是类(class). 一个类确定其他称之为它的实例的对象集的结构和行为. 每一个对象都是一个类的直接实例. 一个对象的类确定可以在这个对象上进行的操作的集合. 关于更多信息, 见章节 4.3 (Classes).
可以写出具有特定于函数参数对象的类的行为的函数. 关于更多信息, 见章节 7.6 (Generic Functions and Methods).
一个对象的类的类称为它的元类(metaclass). 关于元类的更多信息, 见章节 7.4 (Meta-Objects).
4.2 类型
4.2.1 数据类型定义
类型使用的信息所在章节在 Figure 4-1 中指定. Figure 4-7 列出了和对象系统相关的类. Figure 9-1 列出了已定义的状况类型.
章节 | 数据类型 |
---|---|
Section 4.3 (Classes) | Object System types |
Section 7.5 (Slots) | Object System types |
Section 7 (Objects) | Object System types |
Section 7.6 (Generic Functions and Methods) | Object System types |
Section 9.1 (Condition System Concepts) | Condition System types |
Section 4 (Types and Classes) | Miscellaneous types |
Section 2 (Syntax) | All types—read and print syntax |
Section 22.1 (The Lisp Printer) | All types—print syntax |
Section 3.2 (Compilation) | All types—compilation issues |
Figure 4-1. 数据类型信息的 Cross-References
4.2.2 类型关系
类型 cons, symbol, array, number, character, hash-table, function, readtable, package, pathname, stream, random-state, condition, restart, 还有任何由 defstruct, define-condition, 或 defclass 创建的单个类型是互斥的, 除了在 defclass 或者 define-condition 或者 destruct 的 :include 选项指定超类来明确建立类型关系的以外.
扫描二维码关注公众号,回复: 1620915 查看本文章任何由 defstruct 创建的两个类型是互斥的, 除非由于 defstruct 的 :include 选项, 一个是另一个的超类.
任何两个由 defclass 或 define-condition 创建的有区别的类是互斥的, 除非它们有一个共同的子类或者一个类是另一个的超类.
可以扩展一个实现来添加指定类型之间的其他子类型关系, 只要它们不违反这里指定的类型关系和类互斥性的需求. 一个实现可能为任何指定的类型定义额外的子类或超类, 只要不违反互斥性的要求并且每一个额外的类是类型 t 的子类也是类型 nil 的超类.
任凭具体实现自行处理, 无论是 standard-object 还是 structure-object, 都可能出现在一个系统类的优先级列表中, 而该系统类既没有指定 standard-object, 也没有指定 structure-object. 如果确实如此, 它必须在类 t 之前并且后面跟着所有标准化的类.
4.2.3 类型指定符
类型指定符(Type specifier)可以是符号, 类或列表. Figure 4-2 列出了标准原子类型指定符的列表, 并且 Figure 4-3 列出了标准化复合类型指定符的名字. 关于语法信息, 见对应类型指定符的字典条目. 可以通过 defclass, define-condition, defstruct, 或 deftype 来定义新的类型指定符.
arithmetic-error function simple-condition
array generic-function simple-error
atom hash-table simple-string
base-char integer simple-type-error
base-string keyword simple-vector
bignum list simple-warning
bit logical-pathname single-float
bit-vector long-float standard-char
broadcast-stream method standard-class
built-in-class method-combination standard-generic-function
cell-error nil standard-method
character null standard-object
class number storage-condition
compiled-function package stream
complex package-error stream-error
concatenated-stream parse-error string
condition pathname string-stream
cons print-not-readable structure-class
control-error program-error structure-object
division-by-zero random-state style-warning
double-float ratio symbol
echo-stream rational synonym-stream
end-of-file reader-error t
error readtable two-way-stream
extended-char real type-error
file-error restart unbound-slot
file-stream sequence unbound-variable
fixnum serious-condition undefined-function
float short-float unsigned-byte
floating-point-inexact signed-byte vector
floating-point-invalid-operation simple-array warning
floating-point-overflow simple-base-string
floating-point-underflow simple-bit-vector
Figure 4-2. 标准化原子类型指定符
如果一个类型指定符是一个列表, 这个列表的 car 部分是一个符号, 并且这个列表的剩余部分辅助类型信息. 这样一个类型指定符称为复合类型指定符(compound type specifier). 除明确声明外, 辅助项可以是未指明的. 这个未指明的辅助项写作 * 来表示. 比如, 为了完全地指定一个向量序列(vector), 元素的类型和这个序列的长度都必须被指定.
(vector double-float 100)
下面这个留着长度未指定:
(vector double-float *)
下面这个留着元素类型未指定:
(vector * 100)
假设这两种类型指定符是相同的除了第一个有一个 * 而第二个有一个更明确的指定. 那么第二个表示的是第一个表示的子类型.
如果一个列表的末尾有一个或超过一个未指定项, 这些项可以被丢弃. 如果丢弃所有出现的 * 导致一个单元素的列表, 那么圆括号也可以被丢弃 (这个列表可以被它的 car 的符号所替代). 比如, (vector double-float ) 可以被简写为 (vector double-float), 并且 (vector *) 可以被简写为 (vector) 然后是 vector.
and long-float simple-base-string
array member simple-bit-vector
base-string mod simple-string
bit-vector not simple-vector
complex or single-float
cons rational string
double-float real unsigned-byte
eql satisfies values
float short-float vector
function signed-byte
integer simple-array
Figure 4-3. 标准化的复合类型指定符的名字
下面这段展示了可以被用于复合类型指定符名字但是不能被用于原子类型指定符名字的已定义名字.
and mod satisfies
eql not values
member or
Figure 4-4. 标准化的仅限复合类型指定符的名字
新的类型指定符可以以两种方式存在.
- 通过使用 defstruct 不带 :type 指定来定义一个结构或者使用 defclass 来定义一个类或者define-condition 自动导致结构或类的名字成为一个新的类型指定符的符号.
- 可以使用 deftype 来定义派生类型指定符, 它表示的像其他类型指定符的缩写一样.
一个类对象可以被用于一个类型指定符. 当使用这种方式的时候, 它表示这个类的所有成员的集合.
下面这段展示了一些类型和声明相关的定义的名字.
coerce defstruct subtypep
declaim deftype the
declare ftype type
defclass locally type-of
define-condition proclaim typep
Figure 4-5. 类型和声明相关的定义的名字.
下面这段展示了所有定义的类型指定符的名字, 不管是原子类型指定符或者复合类型指定符; 这个列表是 Figure 4-2 和 Figure 4-3 列表的结合.
and function simple-array
arithmetic-error generic-function simple-base-string
array hash-table simple-bit-vector
atom integer simple-condition
base-char keyword simple-error
base-string list simple-string
bignum logical-pathname simple-type-error
bit long-float simple-vector
bit-vector member simple-warning
broadcast-stream method single-float
built-in-class method-combination standard-char
cell-error mod standard-class
character nil standard-generic-function
class not standard-method
compiled-function null standard-object
complex number storage-condition
concatenated-stream or stream
condition package stream-error
cons package-error string
control-error parse-error string-stream
division-by-zero pathname structure-class
double-float print-not-readable structure-object
echo-stream program-error style-warning
end-of-file random-state symbol
eql ratio synonym-stream
error rational t
extended-char reader-error two-way-stream
file-error readtable type-error
file-stream real unbound-slot
fixnum restart unbound-variable
float satisfies undefined-function
floating-point-inexact sequence unsigned-byte
floating-point-invalid-operation serious-condition values
floating-point-overflow short-float vector
floating-point-underflow signed-byte warning
Figure 4-6. 标准化类型指定符的名字
4.3 类
尽管对象系统足够通用来描述所有的标准化类 (包括, 例如, number, hash-table, 和 symbol), 下面这段包含了与理解对象系统相关的类的列表.
built-in-class method-combination standard-object
class standard-class structure-class
generic-function standard-generic-function structure-object
method standard-method
Figure 4-7. 对象系统类
4.3.1 类的介绍
一个类(class) 是一个对象, 它确定其他称之为实例的对象集合的结构和行为.
一个类可以从其他类中继承结构和行为. 一个类出于继承其他类的目的而在定义中引用其他类就称这个类是这些其他类的子类. 为了继承而指定的类称为继承类的超类.
一个类可以有一个名字(name). 函数 class-name 接受一个类对象并且返回它的名字. 一个匿名类的名字是 nil. 一个符号可以命名一个类. 函数 find-class 接受一个符号并且返回这个符号命名的类. 如果名字是一个符号并且如果这个类的名字命名这个类那么这个类有一个特有的名字. 这就是说, 如果 S= (class-name C) 并且 C= (find-class S) 那么一个类 C 有一个特有的名字. 注意, (find-class S1) = (find-class S2) 并且 S1/=S2 是可能的. 如果 C= (find-class S), 我们就说 C 是名为 S 的类.
如果一个类 C2 在它的定义中明确指定 C1 作为超类那么类 C1 就是类 C2 的一个直接超类. 在这个情况下 C2 是 C1 的一个直接子类. 如果 1 <= i < n 并且存在一系列的类 C2,…,Cn-1 而 Ci+1 是 Ci 的直接超类那么类 Cn 是类 C1 的一个超类. 一个类不能被当作是它自身的超类或子类. 这也就是说, 如果 C1 是 C2 的一个超类, 那么 C1 /=C2. 由某个给定的类 C 及其所有超类组成的类集合被称为”C及其超类”.
每一个类有一个类优先级列表(class precedence list), 它是给定类及其超类的集合的总排序. 这个总排序被表示为一个从最具体到最不具体的列表. 这个类优先级列表被用于多种用途. 一般来说, 更具体的类可以遮蔽从不具体的类中继承而来的特性. 方法(method)选择和组合过程使用类优先列表来从最具体到最不具体的顺序排列方法.
当一个类被定义时, 定义表达式形式中提及到的直接超类的顺序是很重要的. 每一个类有一个局部优先级顺序, 这是一个由该类和跟在后面的直接超类组成的列表, 按照定义表达式形式中所提到的顺序.
类优先级列表总是与列表中的每个类的局部优先级顺序一致. 每个局部优先级顺序中的类都以相同的顺序出现在类优先级列表中. 如果局部优先级顺序和其他每一个是不一致的, 就不能构建类优先级列表, 并且发出一个错误. 类优先级列表和它的运算在章节 4.3.5 (Determining the Class Precedence List).
类被组织成一个有向的无环图. 这里有两个显著的类, 名字为 t 和 standard-object. 类 t 没有超类. 它是除了它自身以外所有类的超类. 类 standard-object 是类 standard-class 的一个实例并且是每一个除了它自身以外类 standard-class 实例的超类.
这里由一个从对象系统类空间到类型空间的映射. 很多在这个文档中指定的标准类型都有一个对应的有这类型名字相同的类. 一些类型没有对应的类. 类型和类系统的集成在章节 4.3.7 (Integrating Types and Classes) 中讨论.
类由自身也是类的实例的对象来表示. 对象类的类被称为该对象的元类(metaclass). 当不可能出现错误解释时, 元类这个术语被用来引用一个类, 该类具有自身类的实例. 元类确定作为它实例的类的继承形式, 和那些类实例的表示形式. 这个对象系统提供一个默认的元类, standard-class, 适用于大部分程序.
除另有指定外, 在这个标准中提及的所有类都是类 standard-class 的实例, 所有广义函数都是类 standard-generic-function 的实例, 并且所有方法都是类 standard-method 的实例.
4.3.1.1 标准的元类
对象系统提供了许多预定义的元类. 这些包括类 standard-class, built-in-class, 还有 structure-class:
类 standard-class 是 defclass 定义的类的默认类.
类 built-in-class 是实例具有限制能力的特殊实现的类的类. 任何相当于标准类型的类可能是 built-in-class 的一个实例. 预定义的类型指定符需要有对应的类在 Figure 4-8 中被列出. 这些类中的每一个是否被实现为一个内置的类是依赖于具体实现的.
所有通过 defstruct 定义的类都是类 structure-class 的实例.
4.3.2 定义类
宏 defclass 被用于定义一个新命名的类.
一个类的定义包括:
这个新类的名字. 对于新定义的类这个名字是一个特有的名字.
这个新定义的类的直接超类列表.
一个槽指定符的集合. 每一个槽指定符包括槽的名字和 0 或更多的槽选项. 一个槽选项只适用于单个槽. 如果一个类定义包含两个相同名字的槽指定符, 会发出一个错误.
一个类选项的集合. 每个类选项都属于整个类.
defclass 表达式形式的槽选项和类选项机制被用于:
为给定的槽提供默认的初始值表达式形式.
请求自动生成广义函数的方法,用于读取或写入槽.
控制一个给定的槽是否共享于类的所有实例或者这个类的每个实例是否有它自己的槽.
提供初始化参数和初始化参数默认值, 用于实例的创建.
指定元类而不是默认的. 这个 :metaclass 选项保留给未来使用; 一个实现可以扩展去使用 :metaclass 选项.
指定存储在槽中的期望的类型.
指定槽的文档字符串.
4.3.3 创建类的实例
广义函数 make-instance 创建并返回一个类的一个新的实例. 这个对象系统提供多种机制用于指明一个对象如何被初始化. 比如, 在新创建的对象中通过给 make-instance 提供参数或提供默认的初始化值来指定槽的初始化值是可能的. 进一步的初始化活动可以通过为广义函数编写的方法执行, 这些函数是初始化协议的一部分. 完全的初始化协议在章节 7.1 (Object Creation and Initialization) 中描述.
4.3.4 继承
一个类可以从它的超类中继承方法, 槽, 还有一些 defclass 选项. 其他部分描述了方法的继承, 槽和槽选项的继承以及类选项的继承.
4.3.4.1 继承的示例
(defclass C1 ()
((S1 :initform 5.4 :type number)
(S2 :allocation :class)))
(defclass C2 (C1)
((S1 :initform 5 :type integer)
(S2 :allocation :instance)
(S3 :accessor C2-S3)))
类 C1 的实例有一个局部槽命名为 S1, 它的默认初始值是 5.4 并且它的值应该始终为一个数字. 类 C1 也有一个共享的槽名为 S2.
在 C2 的实例中有一个局部槽名为 S1. S1 的默认初始值为 5. S1 的值应该总是为类型 (and integer number). 在 C2 的实例中也有名为 S2 和 S3 的局部槽. 类 C2 有一个 C2-S3 方法来读取槽 S3 的值; 也有一个 (setf C2-S3) 方法来写入 S3 的值.
4.3.4.2 类选项的继承
这个 :default-initargs 类选项被继承. 一个类的默认初始化参数集合是这个类和它的超类的 :default-initargs 类选项中提供的初始化参数的并集. 当给一个给定的初始化参数提供了不止一个默认初始化值表达式时, 使用的默认初始值表达式形式是由类优先级列表中最具体的类提供的.
如果一个给定的 :default-initargs 类选项不止一次指定相同名字的初始化参数, 会发出一个类型 program-error 的错误.
4.3.5 确定类的优先级列表
一个类的 defclass 表达式形式提供这个类和它的直接超类的完整的列表. 这个列表称之为局部优先级列表(local precedence order). 它是一个这个类和它的直接超类的有序列表. 这个类 C 的类优先级列表是一个由每一个 C 和它的超类的局部优先级列表构成的完整的列表.
一个类在它的直接超类前面, 并且一个直接超类先于那些在 defclass 表达式中的超类列表中在它右边的其他直接超类. 对于每一个类 C, 定义
RC={(C,C1),(C1,C2),…,(Cn-1,Cn)}
其中 C1,…,Cn 是 C 的直接超类,在 defclass 表达式形式提及的列表中. 这些有序对生成类 C 和它的直接超类的总列表.
让 SC 是 C 和它的超类的集合. 让 R 为
R=UcSCRc
这个集合 R 可能或可能不会生成一个部分列表, 取决于 Rc, cSC, 是否是一致的; 假定它们是一致的, R 产生一个部分列表. 当这个 Rc 不是一致的时, 就说 R 是非一致的.
为了计算 C 的优先级列表, 从拓扑上对 SC 的元素排序与 R 产生的部分列表有关. 当这个拓扑排序必须从两个或更多类的集合中选择一个类时, 其中没有一个类的前面有关于R的其他类, 选择的类是确定的, 如下面所述.
如果 R 不是一致的, 会发出一个错误.
4.3.5.1 拓扑排序
拓扑排序是根据 R 中的元素通过在 SC 中找到一个 C 类来进行的, 这样就不会有其他元素先于这个元素. 这个类 C 被放在结果的最前面. 从 SC 中移除 C, 并且从 R 中移除所有表达式 (C,D) 对, DSC. 重复这个过程, 在结果的末尾添加前面没有任何类的类. 当找不到前面没有类的元素时停止.
如果 SC 不是空并且这个过程已经停止, 那么集合 R 是不一致的. 如果在有限集合中的每个类都前置另一个类,那么 R 就包含一个循环. 这就是说, 这里有一个 Ci 先于 Ci+1 的链 C1,…,Cn , 1 <= i < n, 并且 Cn 先于 C1.
有时候这里有多个来自 SC 并且前面没有类的类. 在这个情况下选择迄今为止在类优先级列表中最右边的那个有直接子类的那个. (如果这里没有这样一个候选的类, R 不产生一个部分列表—这个 Rc, cSC, 是不一致的.)
用更精确的术语, 让 {N1,…,Nm}, m>=2, 成为来自于 SC 的前面没有类的类. 让 (C1…Cn), n>=1, 成为目前为止已构建的类优先级列表. C1 是最具体的类, 并且 Cn 最不具体的. 让 1 <= j <= n 成为最大一个数这样这里存在 i 其中 1 <= i <= m 并且 Ni 是 Cj 的一个直接子类; Ni 被放到下一个.
这个规则从一组前面没有类的类中选择的效果是, 一个简单的超类链中的类在类优先级列表中是相邻的, 并且每个相对独立的子图中的类在类优先列表中是相邻的. 例如, 让 T1 和 T2 成为子图, 其唯一公共的元素是类 J. 假设没有 J 的超类出现在 T1 或 T2 中, 并且这个 J 存在于 T1 和 T2 的每个类的超类链中. 让 C1 是 T1 底部; 并且让 C2 是 T2 的底部. 假设这个顺序下 C 的直接超类是 C1 和 C2, 那么 C 的类优先级列表以 C 开始后面更这 T1 的所有类除了 J. 后面是 T2 的所有类. 类 J 和它的超类出现在最后.
4.3.5.2 确定类优先级列表的示例
这个实例确定一个类 pie 的类优先级列表. 定义下面这些类:
(defclass pie (apple cinnamon) ())
(defclass apple (fruit) ())
(defclass cinnamon (spice) ())
(defclass fruit (food) ())
(defclass spice (food) ())
(defclass food () ())
集合 Spie = {pie, apple, cinnamon, fruit, spice, food, standard-object, t}. 集合 R = {(pie, apple), (apple, cinnamon), (apple, fruit), (cinnamon, spice),
(fruit, food), (spice, food), (food, standard-object), (standard-object, t)}.
类 pie 的前面什么都没有, 所以它是第一个; 目前为止结果是 (pie). 从 S 中移除 pie 并且从 R 中移除提及到 pie 的对得到 S = {apple, cinnamon, fruit, spice, food, standard-object, t} 还有 R = {(apple, cinnamon), (apple, fruit), (cinnamon, spice),
(fruit, food), (spice, food), (food, standard-object), (standard-object, t)}.
类 apple 前面没有任何东西, 所以它是下一个; 结果是 (pie apple). 移除 apple 和相关的对得到 S = {cinnamon, fruit, spice, food, standard-object, t} 并且 R = {(cinnamon, spice), (fruit, food), (spice, food), (food, standard-object),
(standard-object, t)}.
类 cinnamon 和 fruit 前面什么都没有, 所以目前为止拥有在类优先级列表中最右边的直接子类的那一个是下一个. 类 apple 是 fruit 的直接子类, 而类 pie 是一个 cinnamon 的直接子类. 因为类优先级列表中 apple 出现 pie 的右边, fruit 是下一个, 目前的结果是 (pie apple fruit). S = {cinnamon, spice, food, standard-object, t}; R = {(cinnamon, spice), (spice, food),(food, standard-object), (standard-object, t)}.
类 cinnamon 是下一个, 目前结果为 (pie apple fruit cinnamon). 这时 S = {spice, food, standard-object, t}; R = {(spice, food), (food, standard-object), (standard-object, t)}.
类 spice, food, standard-object, 还有 t 按这个顺序添加, 然后类优先级列表为 (pie apple fruit cinnamon spice food standard-object t).
编写一组不能被排序的类定义是可能的. 比如:
(defclass new-class (fruit apple) ())
(defclass apple (fruit) ())
类 fruit 必须先于 apple 因为必须保留超类的局部顺序. 类 apple 必须先于 fruit 因为一个类必须先于它的超类. 当这个情况发生时, 会发出一个错误, 当系统尝试去计算 new-class 的类优先级列表时会发生在这里.
以下可能是一组相互冲突的定义:
(defclass pie (apple cinnamon) ())
(defclass pastry (cinnamon apple) ())
(defclass apple () ())
(defclass cinnamon () ())
这个 pie 的类优先级列表是 (pie apple cinnamon standard-object t).
这个 pastry 的类优先级列表是 (pastry cinnamon apple standard-object t).
在 pie 的超类的顺序中 apple 先于 cinnamon 不是问题, 但是在 pastry 中则有问题. 然而, 去构建一个同时有 pie 和 pastry 作为超类的新的类是不可能的.
4.3.6 重定义类
作为一个 standard-class 的直接实例的类可以被重定义, 但是重定义的类也需要是 standard-class 类的实例. 重定义一个类会修改已存在的类对象来反映这个新的类的定义; 它不会为这个类创建一个新的类对象. 任何由旧的 defclass 表达式的 :reader, :writer, 或 :accessor 选项创建的方法对象会从对应广义函数中被移除. 新的 defclass 表达式指定的方法会被添加进去.
当这个类 C 被重定义了, 修改会传递到它的实例以及它的子类的实例. 更新这样一个实例发生的时间依赖于具体实现, 但是不会晚于下一次这个实例的槽被读取或写入. 更新一个实例不会改变函数 eq 定义的它的恒等条件. 更新的过程可能改变这个特别实例的槽, 但是不会创建一个新实例. 更新一个实例是否消耗存储是依赖于具体实现的.
注意, 重定义一个类可能倒是槽被添加或删除. 如果一个类被重新定义, 它改变了实例中可访问的局部槽的集合, 那么实例就会被更新. 如果一个类被重新定义, 它没有改变了实例中可访问的局部槽的集合, 实例是否被更新是依赖于具体实现的.
在旧的类和新的类中都指定为共享的槽的值会被保留. 如果这样一个共享槽在旧的类中没绑定, 它在新的类里也是没有绑定的. 在旧的类中是局部的而在新的类中是共享的槽会被初始化. 新添加的共享槽会被初始化.
每一个新添加的共享槽会被设置为新类的 defclass 表达式中指定的这个槽的初始化表达式的求值结果. 如果这里没有初始化表达式, 这个槽就是未绑定的.
如果一个类被重新定义, 其中类实例中可访问的本地槽的集合被改变, 那么更新这个类的实例的两步式步骤就会发生. 这个过程可能通过调用广义函数 make-instances-obsolete 来明确开始. 这个两步式的过程可以发生在一些实现的其他情况中. 例如, 在一些实现中如果槽在存储中的顺序被改变, 这个两步式过程也会被触发.
第一步通过添加新的局部槽和丢弃这个新的类中没定义的局部槽来修改这个实例的结构. 第二部来初始化新添加的槽并且执行任何其他的用户定义的动作. 这两个步骤在下面两个章节中会进一步说明.
- 4.3.6.1 修改实例的结构
- 4.3.6.2 初始化新添加的局部槽
- 4.3.6.3 定制化类重定义
4.3.6.1 修改实例的结构
第一步修改重定义类的实例的结构来使之符合新的类定义. 在新的类定义中增加的局部槽而在旧的类定义中既没有指定为局部的也不是共享的槽会被添加, 并且在新的类定义中既不是局部也不是共享的而在旧的类定义中指定为局部的槽会被丢弃. 这些新添加和丢弃的槽的名字作为参数传递给下一章节所描述的 update-instance-for-redefined-class.
旧的和新的类中都指定的局部槽的值会被保留. 如果这样一个局部槽是未绑定的, 它就保留为未绑定的.
在旧的类中是共享的而在新的类中指定为局部的槽的值会保留. 如果这样一个共享槽是为绑定的, 这个后来的局部槽也是为绑定的.
4.3.6.2 初始化新添加的局部槽
第二步初始化新添加的局部槽并且执行任何其他用户定义的动作. 这个步骤被广义函数 update-instance-for-redefined-class 实现, 这个函数在第一个修改实例结构的步骤完成后被调用.
广义函数 update-instance-for-redefined-class 需要 4 个必要参数: 在经历过第一个步骤之后要被更新的实例, 添加的局部槽的名称列表, 丢弃的局部槽的名称列表, 还有一个包含丢弃的槽的名字和槽的值的属性列表. 被丢弃的槽中包括旧类中是局部的而新类中是共享的槽.
广义函数 update-instance-for-redefined-class 也接受任意数量的初始化参数. 当它被系统调用来更新类被重定义的实例时, 不会提供初始化参数.
这里有一个系统提供的关于 update-instance-for-redefined-class 主方法, 它的实例参数的指定符是一个 standard-object 类. 首先这个方法检测初始化参数的正确性, 如果一个提供的参数没有被合法声明就会发出一个错误. (关于更多信息, 见章节 7.1.2 (Declaring the Validity of Initialization Arguments).) 然后它调用广义函数 shared-initialize 并传入以下参数: 这个实例, 新添加槽的名称列表, 还有它收到的初始化参数.
4.3.6.3 定制化类重定义
关于 update-instance-for-redefined-class 的方法可能被定义用来指定当一个实例被更新时采取的动作. 如果定义了 update-instance-for-redefined-class 的方法, 那么它们将在 system-supplied 的初始化主方法之后运行, 因此不会影响 update-instance-for-redefined-class 的默认行为. 被系统调用时由于没有传递初始化参数给 update-instance-for-redefined-class, 在 update-instance-for-redefined-class 方法之前添加的槽的初始化表达式形式不会被 shared-initialize 求值.
关于 shared-initialize 的方法可能被定义用来定制类的重定义行为. 关于更多信息, 见章节 7.1.5 (Shared-Initialize).
4.3.7 整合类和类型
对象系统映射类的空间到类型的空间. 每个有着特有的的名字的类都有一个对应相同名字的类型.
每个类的特有的名字是一个合法的类型指定符. 另外, 每个类对象是一个合法的类型指定符. 所以表达式 (typep object class) 在 object 的类是 class 本身或者 class 的子类情况下返回 true. 如果 class1 是 class2 的一个子类或者它们是相同的类, 表达式 (subtypep class1 class2) 的求值返回多值 true 和 true; 否则它返回多值 false 和 true. 如果 I 是某个名为 S 的类 C 的实例并且 C 是 standard-class 的实例, 那么如果 S 是 C 的特有的的名字表达式 (type-of I) 的求值返回 S; 否则, 它返回 C.
由于类的名字和类对象是类型指定符, 它们可能被用于特殊表达式形式 the 还有类型声明.
很多但不是全部预定义的类型指定符都有和类型有着相同特有的名字的类. 这些类型指定符列在 Figure 4-8. 比如, 类型 array 有一个对应的类名为 array. 没有类型指定符是一个列表, 比如 (vector double-float 100), 有着一个对应的类. 操作符 deftype 不会创建任何类.
对应于预定义类型说明符的每个类可以通过以下三种方式实现, 由每个具体实现决定. 它可以是一个 standard class, 一个 structure class, 或者一个 system class.
一个内置的类是一个泛化实例具有限制功能和特殊表示的类. 尝试使用 defclass 去定义 built-in-class 的子类会发出一个错误. 对一个内置类的泛化实例调用 slot-value 会发出一个错误. 重定义一个内置的类或使用 change-class 去改变一个内置类对象的类或把一个对象的类改为内置类都会发出一个错误. 然而, 内置的类可以被用作方法的参数指定符.
可以通过检测元类来确定一个类是否为内置类. 一个标准类是类 standard-class 的实例, 一个内置类是 built-in-class 的实例, 并且一个结构类是 structure-class 的实例.
每一个用 defstruct 创建的没有使用 :type 选项的结构类型都由一个对应的类. 这个类是一个类 structure-class 的泛化实例. 这个 defstruct 的 :include 选项会创建一个对应被包含的结构类型的类的直接子类.
槽是否被牵涉到在这个规范定义的类的实例上的这个规范定义的函数操作中是依赖于具体实现的, 除非这个槽被这个规范明确定义.
如果在一个特定的具体实现中的这个规范定义的类拥有的槽没有在这个规范中定义, 这些槽的名称不能是该规范中定义的包的外部符号, 也不能在 CL-USER 包中访问.
指定许多标准类型指定符有相应的类, 目的是使用户能够编写对这些类型进行区别对待的方法. 方法选择要求为每个类确定一个类优先级列表.
类型指定符之间的层次关系通过与这些类型对应的类之间的关系来反映.
Figure 4-8 列出了预定义类型指定符对应的类的集合.
arithmetic-error generic-function simple-error
array hash-table simple-type-error
bit-vector integer simple-warning
broadcast-stream list standard-class
built-in-class logical-pathname standard-generic-function
cell-error method standard-method
character method-combination standard-object
class null storage-condition
complex number stream
concatenated-stream package stream-error
condition package-error string
cons parse-error string-stream
control-error pathname structure-class
division-by-zero print-not-readable structure-object
echo-stream program-error style-warning
end-of-file random-state symbol
error ratio synonym-stream
file-error rational t
file-stream reader-error two-way-stream
float readtable type-error
floating-point-inexact real unbound-slot
floating-point-invalid-operation restart unbound-variable
floating-point-overflow sequence undefined-function
floating-point-underflow serious-condition vector
function simple-condition warning
Figure 4-8. 对应预定义类型指定符的类
在这些类的条目中指定的类优先级列表信息是对象系统所需要的.
可以扩展单独的具体实现来定义其他类型指定符来拥有相应的类. 单个具体实现可以扩展去添加其他子类关系, 并将其他元素添加到类优先级列表中, 只要它们不违反该标准所指定的类型关系和互斥性需求. 一个已定义的没有指定直接超类的标准类保证和这个表中的所有类都是互斥的, 除了名为 t 的类.
4.4 类型和类的字典
- 类型 NIL
- 类型 BOOLEAN
- 系统类 FUNCTION
- 类型 COMPILED-FUNCTION
- 系统类 GENERIC-FUNCTION
- 系统类 STANDARD-GENERIC-FUNCTION
- 系统类 CLASS
- 系统类 BUILT-IN-CLASS
- 系统类 STRUCTURE-CLASS
- 系统类 STANDARD-CLASS
- 系统类 METHOD
- 系统类 STANDARD-METHOD
- 类 STRUCTURE-OBJECT
- 类 STANDARD-OBJECT
- 系统类 METHOD-COMBINATION
- 系统类 T
- 类型指定符 SATISFIES
- 类型指定符 MEMBER
- 类型指定符 NOT
- 类型指定符 AND
- 类型指定符 OR
- 类型指定符 VALUES
- 类型指定符 EQL
- 函数 COERCE
- 宏 DEFTYPE
- 函数 SUBTYPEP
- 函数 TYPE-OF
- 函数 TYPEP
- 状况类型 TYPE-ERROR
- 函数 TYPE-ERROR-DATUM, TYPE-ERROR-EXPECTED-TYPE
- 状况类型 SIMPLE-TYPE-ERROR
类型 NIL
超类型(Supertypes):
所有类型
描述(Description):
类型 nil 不含任何对象所以也被称为空类型. 类型 nil 是所有类型的子类. 没有 nil 类型的对象.
注意(Notes):
包含对象 nil 的类型是 null, 不是类型 nil.
类型 BOOLEAN
超类型(Supertypes):
boolean, symbol, t
描述(Description):
类型 boolean 包含符号 t 和 nil, 它们分别表示 true 和 false.
也见(See Also):
t (constant variable), nil (constant variable), if, not, complement
注意(Notes):
条件操作, 比如 if, 允许使用广义的 boolean, 不只是 boolean; 任何非 nil 的值, 不只是 t, 对于广义的 boolean 则视作 true. 然而, 作为惯例, 即便对于广义的 boolean 当没有更好的选择来表示它自身时符号 t 被当作正规的值来使用.
系统类 FUNCTION
类优先级列表(Class Precedence List):
function, t
描述(Description):
当适当数量的参数被提供时一个 function 是一个表示要被执行的代码的对象. 一个 function 由 function 特殊表达式, 函数 coerce, 或函数 compile 产生. 一个 function 可以通过把它作为第一个参数给 funcall, apply, 或 multiple-value-call 来直接调用 by using it as the first argument to funcall, apply, or multiple-value-call.
复合类型指定符种类(Compound Type Specifier Kind):
Specializing.
复合类型指定符语法(Compound Type Specifier Syntax):
function [arg-typespec [value-typespec]] arg-typespec::= (typespec* [&optional typespec*] [&rest typespec] [&key (keyword typespec)*])
复合类型指定符的参数(Compound Type Specifier Arguments):
typespec---一个类型指定符. value-typespec---一个类型指定符.
复合类型指定符的描述(Compound Type Specifier Description):
这个 function 类型指定符的列表形式只能被用于声明不能用于辨别(discrimination). 这个类型的每一个元素都是一个函数, 它接受 argj-types 指定类型的参数并返回 value-type 指定类型的返回值. 这里的 &optional, &rest, &key, 和 &allow-other-keys 标记可以出现在参数类型的列表中. 这里 &rest 提供的类型指定符是每一个实际参数的类型, 不是对应变量的类型.
这个 &key 参数应该像 (keyword type) 这样的表达式的列表来提供. 这个 keyword 必须是一个合法的关键字名字符号, 必须在调用的实际参数中提供. 这个通常是 KEYWORD 包中的符号但是可以是任何符号. 当 &key 在一个 function 类型指定符的 lambda 列表中被提供, 这个给定的关键字参数必须是详尽的除非 &allow-other-keys 也被提供. &allow-other-keys 是一个指示符表示其他的关键字参数实际中可以被提供, 如果提供了, 就可以被使用. 比如, 函数 make-list 类型可以按以下的方式来声明:
(function ((integer 0) &key (:initial-element t)) list)
这个 value-type 可以是一个 values 类型指定符用来表示多值的类型.
细想以下表达式的一个声明:
(ftype (function (arg0-type arg1-type ...) val-type) f))
任何在这个声明的作用域中的 (f arg0 arg1 …) 表达式等价于以下:
(the val-type (f (the arg0-type arg0) (the arg1-type arg1) ...))
这也就是说, 如果任意参数不是指定的类型或者结果不是指定的类型, 那么结果是不可预料的. 具体来说, 如果任意参数不是正确的类型, 结果就不保证是指定的类型.
因此, 一个函数的 ftype 声明描述了这个函数的调用, 不是这个函数实际的定义.
细想一个以下表达式的声明:
(type (function (arg0-type arg1-type ...) val-type) fn-valued-variable)
这个声明有着这样的解释, 在这个声明的作用域里, 如果 fn-valued-variable 的值被调用, 参数不是指定的类型, 那么结果是不可预料的; 一个合法调用的结果值会是 val-type 类型.
与变量类型声明一样, 嵌套声明意味着类型的交集, 如下:
细想以下两种 ftype 的声明:
(ftype (function (arg0-type1 arg1-type1 ...) val-type1) f))
还有
(ftype (function (arg0-type2 arg1-type2 ...) val-type2) f))
如果这些声明都生效, 那么在这些声明的共享作用域内, 对 f 的调用可以被认为好像 f 是按下面这种方式声明的:
(ftype (function ((and arg0-type1 arg0-type2) (and arg1-type1 arg1-type2 ...) ...) (and val-type1 val-type2)) f))
它被允许忽略一个或全部的 ftype 声明.
如果一个变量的两个 (or more) 类型声明生效, 并且它们都是函数声明, 这些声明也会类似地组合.
类型 COMPILED-FUNCTION
超类型(Supertypes):
compiled-function, function, t
描述(Description):
如果一个函数不包含在运行时必须展开的宏的引用, 并且它也不包含加载时值的未解析的引用, 那么任何函数都可以被认为是一个编译后的函数(compiled function). 见章节 3.2.2 (Compilation Semantics). 一个函数的定义词法上出现在一个文件中, 这个文件已经被 compile-file 编译并且被 load 加载后这个函数的类型就是 compiled-function. compile 函数产生的函数也是 compiled-function 类型的. 其他函数也可能是 compiled-function 类型.
系统类 GENERIC-FUNCTION
类优先级列表(Class Precedence List):
generic-function, function, t
描述(Description):
一个广义函数是一个行为取决于提供给它的参数的标识或类. 一个广义函数对象包含一个方法的集合, 一个 lambda 列表, 一个方法组合类型, 还有其他信息. 方法定义了广义函数的类特定的行为和操作; 一个方法也被称为特化一个广义函数. 当被调用时, 一个广义函数基于它的参数的类和标识去执行它的方法的一个子集. 一个广义函数可以和普通函数相同的方式被使用; specifically, 一个广义函数可以被用作 funcall 和 apply 的参数, 并且可以被赋予一个全局或局部的名字.
系统类 STANDARD-GENERIC-FUNCTION
类优先级列表(Class Precedence List):
standard-generic-function, generic-function, function, t
描述(Description):
类 standard-generic-function 是 defmethod, ensure-generic-function, defgeneric, 和 defclass 表达式建立的默认广义函数的类.
系统类 CLASS
类优先级列表(Class Precedence List):
class, standard-object, t
描述(Description):
类型 class 表示确定它们实例的结构的行为的对象. 与 class 类型的对象相关联的信息是描述其在类的非循环图形中的位置、它的槽还有它的选项的信息.
系统类 BUILT-IN-CLASS
类优先级列表(Class Precedence List):
built-in-class, class, standard-object, t
描述(Description):
一个内置类是一个其实例具有限制功能和特殊表示的类. 尝试用 defclass 去定义一个内置类的子类会发出一个 error 类型的错误. 调用 make-instance 去创建一个内置类的实例会发出一个 error 类型的错误. 在一个内置类的实例上调用会发出一个 error 类型的错误. 重定义一个内置类或使用 change-class 去改变一个实例的类为内置类或改变一个内置类为其他类会发出一个 error 的错误. 然而, 内置类可以被用作方法的参数指定符.
系统类 STRUCTURE-CLASS
类优先级列表(Class Precedence List):
structure-class, class, standard-object, t
描述(Description):
所有通过 defstruct 定义的类的都是类 structure-class 的实例.
系统类 STANDARD-CLASS
类优先级列表(Class Precedence List):
standard-class, class, standard-object, t
描述(Description):
类 standard-class 是 defclass 定义出来的默认类.
系统类 METHOD
类优先级列表(Class Precedence List):
method, t
描述(Description):
一个方法是表示一个广义函数的行为的模组化部分的对象. 一个方法包含了实现这个方法行为的代码, 一个指定什么时候这个给定方法可以被应用的参数指定符序列, 还有一个被用于方法组合机制来辨别方法的限定符序列. 每一个方法的每一个必要参数都有一个关联的参数指定符, 并且只有在参数满足方法的参数指定符时, 该方法才会被调用. 方法组合机制控制方法的选择, 它们执行的顺序, 还有广义函数返回的值. 对象系统提供一个默认方法组合类型并且提供一个机制来声明新的方法组合类型.
也见(See Also):
章节 7.6 (Generic Functions and Methods)
系统类 STANDARD-METHOD
类优先级列表(Class Precedence List):
standard-method, method, standard-object, t
描述(Description):
这个类 standard-method 是 defmethod 和 defgeneric 表达式定义的方法的默认类.
类 STRUCTURE-OBJECT
类优先级列表(Class Precedence List):
structure-object, t
描述(Description):
类 structure-object 是 structure-class 的一个实例并且是 structure-class 实例的每一个类的超类除了它自身, 并且是 defstruct 定义的每一个类的超类.
也见(See Also):
defstruct, Section 2.4.8.13 (Sharpsign S), Section 22.1.3.12 (Printing Structures)
类 STANDARD-OBJECT
类优先级列表(Class Precedence List):
standard-object, t
描述(Description):
类 standard-object 是 standard-class 的一个实例并且是 standard-class 的实例的每个类的超类除了它自身.
系统类 METHOD-COMBINATION
类优先级列表(Class Precedence List):
method-combination, t
描述(Description):
每一个方法组合对象是类 method-combination 的间接实例. 一个方法组合对象表示这个方法组合被一个广义函数所使用的信息. 一个方法组合对象包含了方法组合的类型还有这个类型要使用的参数信息.
系统类 T
类优先级列表(Class Precedence List):
t
描述(Description):
所有对象的集合. 类型 t 每个类型的超类型, 包括它自身. 每个对象都是类型 t.
类型指定符 SATISFIES
复合类型指定符种类(Compound Type Specifier Kind):
Predicating.
复合类型指定符语法(Compound Type Specifier Syntax):
satisfies predicate-name
复合类型指定符的参数(Compound Type Specifier Arguments):
predicate-name---一个符号.
复合类型指定符的描述(Compound Type Specifier Description):
这个表示满足断言 predicate-name 的所有对象的集合, 这个断言必须是单个参数断言的全局函数定义的符号. predicate-name 需要一个名字;不允许 lambda 表达式. 比如, 类型指定符 (and integer (satisfies evenp)) 表示偶数整型的集合. 表达式 (typep x '(satisfies p)) 等价于 (if (p x) t nil). 这个参数是必要的. 符号 * 可以是参数, 但是它表示它自身 (the symbol *), 不表示一个未指定的类型. 符号 satisfies 作为类型指定符是不合法的.
类型指定符 MEMBER
复合类型指定符种类(Compound Type Specifier Kind):
Combining.
复合类型指定符语法(Compound Type Specifier Syntax):
member object*
复合类型指定符的参数(Compound Type Specifier Arguments):
object---一个对象.
复合类型指定符的描述(Compound Type Specifier Description):
这表示这个集合包含 objects 命名的对象. 一个对象只有在它 eql 指定对象中的其中一个时才是这个类型. 类型指定符 (member) 和 nil 是等价的. * 可以在 objects 之中, 但是如果这样的话它表示它自身 (符号 *) 并且不表示一个未指定的值. 符号 member 作为类型指定符是非法的; 并且, 特别指出, 它既不是 (member) 的缩写也不是 (member *) 的缩写.
也见(See Also):
类型 eql
类型指定符 NOT
复合类型指定符种类(Compound Type Specifier Kind):
Combining.
复合类型指定符语法(Compound Type Specifier Syntax):
not typespec
复合类型指定符的参数(Compound Type Specifier Arguments):
typespec---一个类型指定符.
复合类型指定符的描述(Compound Type Specifier Description):
这表示所有对象的集合都不是类型 typespec. 这个参数是必要的, 并且不能是 *. 符号 not 作为类型指定符是非法的.
类型指定符 AND
复合类型指定符种类(Compound Type Specifier Kind):
Combining.
复合类型指定符语法(Compound Type Specifier Syntax):
and typespec*
复合类型指定符的参数(Compound Type Specifier Arguments):
typespec---一个类型指定符.
复合类型指定符的描述(Compound Type Specifier Description):
这表示所有对象的集合都是 typespecs 的交集所确定的类型. * 不允许作为参数. 类型指定符 (and) 和 t 是等价的. 符号 and 作为一个类型指定符是不合法的, 并且, 特别指出, 它不是 (and) 的一个缩写.
类型指定符 OR
复合类型指定符种类(Compound Type Specifier Kind):
Combining.
复合类型指定符语法(Compound Type Specifier Syntax):
or typespec*
复合类型指定符的参数(Compound Type Specifier Arguments):
typespec---一个类型指定符.
复合类型指定符的描述(Compound Type Specifier Description):
这表示所有对象集合都是 typespecs 的并集确定的类型. 比如, 类型 list 定义等价于 (or null cons). Also, 通过 position 返回的值是类型 (or null (integer 0 *)) 的一个对象; 换句化说, 可以是 nil 或者一个非负整数. * 不允许作为一个参数. 类型指定符 (or) 和 nil 是等价的. 符号 or 作为类型指定符是非法的; 并且, 特别指出, 这个不是 (or) 的缩写.
类型指定符 VALUES
复合类型指定符种类(Compound Type Specifier Kind):
Specializing.
复合类型指定符语法(Compound Type Specifier Syntax):
values value-typespec value-typespec::= typespec* [&optional typespec*] [&rest typespec] [&allow-other-keys]
复合类型指定符的参数(Compound Type Specifier Arguments):
typespec---一个类型指定符.
复合类型指定符的描述(Compound Type Specifier Description):
这个类型指定符只能被用作一个 function 类型指定符或一个 the 特殊表达式的 value-type 部分. 当涉及到多值时它被用于指定每个值单独的类型. 这个 &optional 和 &rest 标记可以出现在 value-type 列表中; 它们指定和值一起传递给 multiple-value-call 的一个函数的参数列表会正确地接收到那些值. 符号 * 不能在 value-types 之中. 符号 values 作为类型指定符是不合法的; 并且, 特别指出, 它不是 (values) 的缩写.
类型指定符 EQL
复合类型指定符种类(Compound Type Specifier Kind):
Combining.
复合类型指定符语法(Compound Type Specifier Syntax):
eql object
复合类型指定符的参数(Compound Type Specifier Arguments):
object---一个对象.
复合类型指定符的描述(Compound Type Specifier Description):
表示对于 (eql object x) 为 true 的所有 x 的类型. 参数 object 是必要的. 这个 object 可以是 *, 但是如果是这样它表示它自身(符号 *) 并且不表示一个未指定的值. 符号 eql 作为一个原子类型指示符是不合法的.
函数 COERCE
语法(Syntax):
coerce object result-type => result
参数和值(Arguments and Values):
object---一个对象. result-type---一个类型指定符. result---一个类型 result-type 的对象, 除了在章节 12.1.5.3 (Rule of Canonical Representation for Complex Rationals) 所描述的情况外.
描述(Description):
强制 object 为类型 result-type. 如果 object 已经是类型 result-type, 返回 object 自身, 一般不管是否会有可能强制一些其他类型的对象为 result-type. 否则, 这个 object 根据以下规则强制为类型 result-type:
sequence
如果这个 result-type 是 list 的一个可识别的子类型, 并且这个 object 是一个 sequence, 那么这个 result 是一个和 object 有者相同元素的 list. 如果 result-type 是 vector 的一个可识别子类型, 并且这个对象是一个 sequence, 那么这个 result 一个和 object 有着相同元素的 vector. 如果 result-type 是一个特化的类型, 那么 result 会有一个实际数组元素类型, 它是对特化类型的元素类型进行升级的结果. 如果没有指定元素类型, 那么这个元素类型默认是 t. 如果具体实现不能确定元素类型, 会发出一个错误.
character
如果这个 result-type 是 character 并且这个 object 是一个字符标识符, 那么这个 result 是它表示的 character.
complex
如果这个 result-type 是 complex 并且这个 object 是一个 real, 那么这个 result 是通过构造一个实部是 object 并且虚部是将一个整数0强制转为 object 类型的结果(使用 coerce)的complex 获取到的. (然而, 如果实部是一个有理数, 那么结果一个被表示为一个有理数而不是一个复数; 见章节 12.1.5.3 (Rule of Canonical Representation for Complex Rationals). 所以, 比如, (coerce 3 'complex) 允许的, 但是会返回 3, 它不是一个 complex.)
float
如果 result-type 是 float, short-float, single-float, double-float, long-float 中的任何一个, 并且这个 object 是一个 real, 那么 result 是一个 result-type 类型的浮点数, 无论这个浮点表示法允许的是多大的具象精度, 它都和 object 的符号和大小是相等的. (如果这个 result-type 是 float 并且 object 还不是一个 float, 那么这个 result 是一个 single-float.)
function
如果 result-type 是 function, 并且 object 是任何 fbound 的函数名字但是既不是全局定义的宏名字也不是特殊操作符, 那么这个 result 是 object 的函数值. 如果 result-type 是 function, 并且 object 是一个 lambda 表达式, 那么这个 result 是 object 在 null 词法环境的一个闭包.
t
任何 object 可以被强制转为 t 类型的对象. 这个情况下, 这个 object 被简单地返回.
示例(Examples):
(coerce '(a b c) 'vector) => #(A B C) (coerce 'a 'character) => #\A (coerce 4.56 'complex) => #C(4.56 0.0) (coerce 4.5s0 'complex) => #C(4.5s0 0.0s0) (coerce 7/2 'complex) => 7/2 (coerce 0 'short-float) => 0.0s0 (coerce 3.5L0 'float) => 3.5L0 (coerce 7/2 'float) => 3.5 (coerce (cons 1 2) t) => (1 . 2)
All the following forms should signal an error:
(coerce '(a b c) '(vector * 4)) (coerce #(a b c) '(vector * 4)) (coerce '(a b c) '(vector * 2)) (coerce #(a b c) '(vector * 2)) (coerce "foo" '(string 2)) (coerce #(#\a #\b #\c) '(string 2)) (coerce '(0 1) '(simple-bit-vector 3))
受此影响(Affected By): None.
异常情况(Exceptional Situations):
如果一个强制转换是不可以的, 会发出一个 type-error 类型的错误. (coerce x 'nil) 总是发出一个 type-error 类型的错误. 如果 result-type 是一个 function 但是 object 是一个没有 fbound 的符号或者这个符号命名一个宏或特殊操作符, 那么就会发出一个 error 类型的错误. 如果 result-type 指定的元素数量和 object 是不同长度的, 那么 type-error 类型的错误应该被发出.
也见(See Also):
rational, floor, char-code, char-int
注意(Notes):
由于舍入问题, 没有提供从浮点数到有理数和从比率到整数的强制转换.
(coerce x 't) == (identity x) == x
宏 DEFTYPE
语法(Syntax):
deftype name lambda-list [[declaration* | documentation]] form* => name
参数和值(Arguments and Values):
name---一个符号. lambda-list---一个 deftype lambda 列表. declaration---一个 declare 表达式; 不求值. documentation---一个字符串; 不求值. form---一个表达式形式.
描述(Description):
deftype 定义一个名为 name 的派生类型指定符. 新的类型指定符的意义在于一个函数, 它将类型指定符展开为另一个类型指定符, 如果另一个类型指定符自身包含对另一个派生类型指定符的引用, 它本身就会被展开. 新定义的类型指定符可以作为 (name arg1 arg2 ...) 表达式的一个列表来引用. 参数的数量必须和 lambda-list 一样. 如果新的类型指定符不接受参数, 或者它的所有参数是可选的, 这个类型指定符可以被用作原子类型指定符. 给这个类型指定符的参数表达式, arg1 ... argn, 是不求值的. 相反, 这些字面化对象变成了相应的参数被绑定的对象. 这个 deftype 表达式主体部分(不是 lambda-list) 隐含在一个名为 name 的块中, 并且作为一个隐式 progn 被求值, 返回一个新的类型指定符. 这个主体部分的词法环境是 deftype 表达式形式被求值是的当前那个, 由 lambda-list 中的变量来扩展. 当展开必须终止时返回类型指定符的递归展开, 包括在展开中嵌套的类型指定符的展开. 如果完全展开类型指定符的结果包含任何环状结构, 那么其结果是未定义的, 除非是在 member 和 eql 类型指定的对象中. 这个 documentation 作为 type 种类的文档字符串关联到 name. 如果一个 deftype 表达式作为顶层表达式出现, 编译器必须确保 name 在后续类型声明中被识别. 如果这个 name 在后续类型声明中被引用, 那么程序员必须确保这个 deftype 表达式的主体部分可以在编译时被求值. 如果一个类型指定符的展开没有在编译时被完全定义 (或许是因为它展开为一个未知类型指定符或者一个命名函数的 satisfies 没有在这个编译时环境中定义), 一个具体实现可能忽略任何声明中这个类型的引用 并且/或者 发出一个警告.
示例(Examples):
```LISP (defun equidimensional (a) (or (< (array-rank a) 2) (apply #'= (array-dimensions a)))) => EQUIDIMENSIONAL (deftype square-matrix (&optional type size) `(and (array ,type (,size ,size)) (satisfies equidimensional))) => SQUARE-MATRIX ```
副作用(Side Effects): None.
受此影响(Affected By): None.
异常情况(Exceptional Situations): None.
也见(See Also):
declare, defmacro, documentation, Section 4.2.3 (Type Specifiers), Section 3.4.11 (Syntactic Interaction of Documentation Strings and Declarations)
注意(Notes): None.
函数 SUBTYPEP
语法(Syntax):
subtypep type-1 type-2 &optional environment => subtype-p, valid-p
参数和值(Arguments and Values):
type-1---一个类型指定符. type-2---一个类型指定符. environment---一个环境对象. 默认是 nil, 表示 null 的词法环境和当前的全局环境. subtype-p---一个广义的 boolean. valid-p---一个广义的 boolean.
描述(Description):
如果 type-1 是 type-2 的一个可识别的子类型, 第一个值就是 true. 否则, 第一个值就是 false, 表示 type-1 不是 type-2 的子类, 或者 type-1 是 type-2 的子类但是不是一个可识别的子类型. 返回的第二个值表示第一个值的确定性. 如果这个值是 true, 那么第一个值就是子类型关系的精确表示. (当第一个值是 true 时第二个值总是为 true.) 下面这段总结了返回值的可能的组合. Value 1 Value 2 Meaning true true type-1 确定是 type-2 的子类. false true type-1 确定不是 type-2 的子类. false false subtypep 不能确定关系,所以 type-1 可能或可能不是 type-2 的子类. Figure 4-9. subtypep 结果的可能性 subtypep 只有当至少一个参数涉及到后面其中一个类型指定符时才允许返回 false 和 false 的多值: and, eql, function 的列表表达式, member, not, or, satisfies, or values. (一个类型指定符'包含'这样一个符号, 在类型被展开后, 它在一个位置中包含那个符号, 它可以将它的含义作为一种类型指定符来使用it contains that symbol in a position that would call for its meaning as a type specifier to be used.) 一个可能的推论是, 如果 type-1 和 type-2 都不涉及这些类型指定符, 那么 subtypep 不得不去准确确定关系. 具体来说, 如果参数是 equal 的并且不涉及任何这些类型指定符时返回 true 和 true 的多值. 当 type-1 和 type-2 只涉及 Figure 4-2 中的名字, 或者 defstruct, define-condition, 或 defclass 定义的类型的名字, 或者只展开到那些名字的衍生类型时, subtypep 第二个返回值一定不是 nil. 当列在 Figure 4-2 的类型指定符还有 defclass 和 defstruct 名称在一些情况下被实现为衍生类型时, subtypep 把它们当作原语(primitive). subtypep 所反映的类型之间的关系是特定于具体实现的. 比如, 如果一个实现只支持浮点数的单个类型, 在那个实现中 (subtypep 'float 'long-float) 返回 true 和 true 多值(因为两个类型是一样的). 对于所有除了 * 以外的 T1 和 T2, 当且仅当 (array T1) 和 (array T2) 指向相同特化表示的数组时, 它们是总是指向相同集合的两种不同的类型指定符, 换句话说, 如果 (upgraded-array-element-type 'T1) 和 (upgraded-array-element-type 'T2) 返回两个指向相同对象集合的不同类型指定符. 这是 `(array type-specifier) 和 `(array ,(upgraded-array-element-type 'type-specifier)) 指向相同特化数组表示的另一种说法. 对于所有除了 * 以外的 T1 和 T2, 当且仅当 (array T1) 和 (array T2) 指向不同有区别特化表示的数组时, 它们的交集是个空集合. 因此,
(subtypep '(array T1) '(array T2)) => true
当且仅当 (upgraded-array-element-type 'T1) 和 (upgraded-array-element-type 'T2) 返回两个指向相同对象的集合的不同类型指定符的时候. 对于所有除了 * 以外的类型指定符 T1 和 T2,
(subtypep '(complex T1) '(complex T2)) => true, true
如果: 1. T1 是 T2 的子类型, 或者 2. (upgraded-complex-part-type 'T1) 和 (upgraded-complex-part-type 'T2) 返回指向相同对象集合的不同类型指定符; 这个情况下, (complex T1) 和 (complex T2) 都指向相同的特化表示. 否则值就是 false 和 true. 表达式
(subtypep '(complex single-float) '(complex float))
在所有实现一定返回 true, 但是
(subtypep '(array single-float) '(array float))
只有在没有为单浮点和其他浮点数作区分的特化数组表示的具体实现中返回 true.
示例(Examples):
(subtypep 'compiled-function 'function) => true, true (subtypep 'null 'list) => true, true (subtypep 'null 'symbol) => true, true (subtypep 'integer 'string) => false, true (subtypep '(satisfies dummy) nil) => false, implementation-dependent (subtypep '(integer 1 3) '(integer 1 4)) => true, true (subtypep '(integer (0) (0)) 'nil) => true, true (subtypep 'nil '(integer (0) (0))) => true, true (subtypep '(integer (0) (0)) '(member)) => true, true ;or false, false (subtypep '(member) 'nil) => true, true ;or false, false (subtypep 'nil '(member)) => true, true ;or false, false
让 <aet-x> 和 <aet-y> 是两个有区别的类型指定符, 在一个给定实现中不总是指向相同对象的集合, 但是 make-array 对于它们, 会返回一个相同数组类型的对象. 因此, 在每种情况下,
(subtypep (array-element-type (make-array 0 :element-type '<aet-x>)) (array-element-type (make-array 0 :element-type '<aet-y>))) => true, true (subtypep (array-element-type (make-array 0 :element-type '<aet-y>)) (array-element-type (make-array 0 :element-type '<aet-x>))) => true, true
如果 (array <aet-x>) 和 (array <aet-y>) 是同一组对象的不同名称, 这些名字应该指向相同的一组对象. 这意味着下面的测试也是正确的:
(subtypep '(array <aet-x>) '(array <aet-y>)) => true, true (subtypep '(array <aet-y>) '(array <aet-x>)) => true, true
副作用(Side Effects): None.
受此影响(Affected By): None.
异常情况(Exceptional Situations): None.
也见(See Also):
Section 4.2 (Types)
注意(Notes):
对于 array 和 complex 类型, subtypep 规范中细微的差异是有必要的, 因为这里没有 complex 的创建函数允许产生的各个部分类型独立于这些部分的实际类型. 因此,在 complex 类型的情况下, 虽然一个数字可以是多个类型, 但引用的是它的实际类型. 比如, 17 是类型 (mod 18) 也是类型 (mod 256) 并且也是 integer 类型; 还有 2.3f5 是类型 single-float 也是类型 float.
函数 TYPE-OF
语法(Syntax):
type-of object => typespec
参数和值(Arguments and Values):
object---一个对象. typespec---一个类型指定符.
描述(Description):
返回一个类型指定符, typespec, 用于将 object 作为元素的类型. 这个 typespec 满足以下条件:
对于任何内置类型的元素的对象:
a. 返回的类型是一个内置类型的可识别的子类型.
b. 返回的类型没有涉及 and, eql, member, not, or, satisfies, 或 values.
对于所有对象, (typep object (type-of object)) 返回 true. 其中隐含的是类型指定符不适用于typep, 比如 function 类型指定符的列表表达式是从来不会被 type-of 返回的.
type-of 返回的类型总是为 class-of 返回的类的可识别子类型. 这也就是说,
(subtypep (type-of object) (class-of object)) => true, true
对于元类 structure-class 或 standard-class 的对象, 还有 conditions, type-of 返回那个 class-of 返回的类的特有的名字, 如果有的话, 否则返回类本省. 具体来说, 对于 defstruct 不带 :type 选项定义的结构体的构造器函数创建的对象, type-of 返回结构体的名字; 对于 make-condition 创建的对象, 这个 typespec 状况类型的名字.
对于类型 short-float, single-float, double-float, 或 long-float 中的每一个, 如果 object 是其中一个的元素, 这个 typespec 是那个类型的可识别子类型.
示例(Examples):
(type-of 'a) => SYMBOL (type-of '(1 . 2)) => CONS OR=> (CONS FIXNUM FIXNUM) (type-of #c(0 1)) => COMPLEX OR=> (COMPLEX INTEGER) (defstruct temp-struct x y z) => TEMP-STRUCT (type-of (make-temp-struct)) => TEMP-STRUCT (type-of "abc") => STRING OR=> (STRING 3) (subtypep (type-of "abc") 'string) => true, true (type-of (expt 2 40)) => BIGNUM OR=> INTEGER OR=> (INTEGER 1099511627776 1099511627776) OR=> SYSTEM::TWO-WORD-BIGNUM OR=> FIXNUM (subtypep (type-of 112312) 'integer) => true, true (defvar *foo* (make-array 5 :element-type t)) => *FOO* (class-name (class-of *foo*)) => VECTOR (type-of *foo*) => VECTOR OR=> (VECTOR T 5)
受此影响(Affected By): None.
异常情况(Exceptional Situations): None.
也见(See Also):
array-element-type, class-of, defstruct, typecase, typep, Section 4.2 (Types)
注意(Notes):
鼓励实现者去安排 type-of 返回一个可移植的值.
函数 TYPEP
语法(Syntax):
typep object type-specifier &optional environment => generalized-boolean
参数和值(Arguments and Values):
object---一个对象. type-specifier---任何除了 values 以外的类型指定符, 或者一个第一个元素为 function 或 values 的类型指定符列表. environment---一个环境对象. 默认是 nil, 表示 null 的词法环境和当前的全局环境. generalized-boolean---一个广义的 boolean.
描述(Description):
如果 object 是类型指定符指定的类型就返回 true;否则, 返回 false. 表达式 (satisfies fn) 作为 type-specifier 的话, 就通过应用函数 fn 给 object 来处理. (typep object '(array type-specifier)), 其中 type-specifier 不是 *, 当且仅当 object 是一个提供的 type-specifier 作为 make-array 的 :element-type 参数所构建出来的数组时返回 true. (array *) 指向所有数组不管其元素类型, 而 (array type-specifier) 只指向那些可以通过把 type-specifier 作为 make-array 的 :element-type 参数所构建出来的数组. 一个类似的解释可以应用于 (simple-array type-specifier) 和 (vector type-specifier). 见章节 15.1.2.1 (Array Upgrading). (typep object '(complex type-specifier)) 对于所有可以通过给 type-specifier 类型的数字到函数 complex 来获取到的复数返回 true, 附加其他相同特化表示的复数. 任何这样的复数的实部和虚部必须满足:
(typep realpart 'type-specifier) (typep imagpart 'type-specifier)
见函数 upgraded-complex-part-type.
示例(Examples):
(typep 12 'integer) => true (typep (1+ most-positive-fixnum) 'fixnum) => false (typep nil t) => true (typep nil nil) => false (typep 1 '(mod 2)) => true (typep #c(1 1) '(complex (eql 1))) => true ;; To understand this next example, you might need to refer to ;; Section 12.1.5.3 (Rule of Canonical Representation for Complex Rationals). (typep #c(0 0) '(complex (eql 0))) => false
让 Ax 和 Ay 为表示不同类型的类型指定符, 但是对于它们
(upgraded-array-element-type 'Ax)
和
(upgraded-array-element-type 'Ay)
表示相同的类型. 注意
(typep (make-array 0 :element-type 'Ax) '(array Ax)) => true (typep (make-array 0 :element-type 'Ay) '(array Ay)) => true (typep (make-array 0 :element-type 'Ax) '(array Ay)) => true (typep (make-array 0 :element-type 'Ay) '(array Ax)) => true
受此影响(Affected By): None.
异常情况(Exceptional Situations):
如果 type-specifier 是 values, 或者第一个元素是 function 或 values 的类型指定符列表, 那么就会发出一个 error 类型的错误. 如果 type-specifier 不是一个类型指定符, 那么结果是未定义的.
也见(See Also):
type-of, upgraded-array-element-type, upgraded-complex-part-type, Section 4.2.3 (Type Specifiers)
注意(Notes):
鼓励实现者去识别和优化 (typep x (the class y)) 的情况, 因为它不需要在运行时展开 deftype 信息.
状况类型 TYPE-ERROR
类优先级列表(Class Precedence List):
type-error, error, serious-condition, condition, t
描述(Description):
类型 type-error 表示一个对象不是期望类型的情况. 这个违反基准(offending datum)和期望类型(expected type)被 make-condition 的名为 :datum 和 :expected-type 的参数所初始化, 并且被函数 type-error-datum 和 type-error-expected-type 所访问.
也见(See Also):
type-error-datum, type-error-expected-type
函数 TYPE-ERROR-DATUM, TYPE-ERROR-EXPECTED-TYPE
语法(Syntax):
type-error-datum condition => datum type-error-expected-type condition => expected-type
参数和值(Arguments and Values):
condition---一个 type-error 类型的状况. datum---一个对象. expected-type---一个类型指定符.
描述(Description):
type-error-datum 返回 condition 表示的状况的违反基准. type-error-expected-type 返回 condition 表示的违反基准的期望类型.
示例(Examples):
“`LISP
(defun fix-digits (condition)
(check-type condition type-error)
(let* ((digits ‘(zero one two three four
five six seven eight nine))
(val (position (type-error-datum condition) digits)))
(if (and val (subtypep ‘fixnum (type-error-expected-type condition)))
(store-value 7))))(defun foo (x)
(handler-bind ((type-error #’fix-digits))
(check-type x number)
(+ x 3)))(foo ‘seven)
=> 10
“`LISP副作用(Side Effects): None.
受此影响(Affected By): None.
异常情况(Exceptional Situations): None.
也见(See Also):
type-error, Section 9 (Conditions)
注意(Notes): None.
状况类型 SIMPLE-TYPE-ERROR
类优先级列表(Class Precedence List):
simple-type-error, simple-condition, type-error, error, serious-condition, condition, t
描述(Description):
类型 simple-type-error 的状况类似类型 type-error 的状况, 除了它们提供了另一种机制来指定如何报告状况; 见类型 simple-condition.
也见(See Also):
simple-condition, simple-condition-format-control, simple-condition-format-arguments, type-error-datum, type-error-expected-type