Common Lisp对象系统

Common Lisp对象系统（CLOS）是一种面向对象编程设施，它是ANSI Common Lisp的一部分。CLOS是强力的动态对象系统，在根本上不同于静态语言，比如C++或Java中的那种OOP设施。CLOS受到更早期的Lisp对象系统，比如MIT Flavors (编程语言)和Xerox CommonLoops的启发，然而它比二者要更加泛化。

## 历史

CLOS最初被提议作为一种附加件，首次出现在1988年发表的《Common Lisp面向对象编程》，和1990年出版的《Common Lisp语言》第二版（也叫做CLtL2）中。在1994年，CLOS被接受为ANSI标准Common Lisp的一部分，并且已经被适配入其他Lisp方言比如EuLisp和Emacs Lisp之中。

## 特征

CLOS的基本建造块是类、方法、泛化函数和类的实例。CLOS提供了定义它们的宏：`defclass`、`defmethod`和`defgeneric`。实例通过方法`make-instance`来创建。CLOS是基于类的编程语言，并非基于原型的编程语言，类必须声明于对象可以实例化为这个类的成员之前。

### 类

类是槽的一个列表，它可以有多个超类，和一个特定的元类。槽在C++/Java中被称为成员变量，它可以由类或实例来分配，类所分配的槽由其所有实例共享。每个槽都有一个名字，而一个槽的值可以使用函数`slot-value`，通过这个名字来访问。可以定义额外的特定泛化函数，来写或读这些槽的值。在CLOS类中的每个槽，都必须有一个唯一的名字。

如同多数动态语言中的对象系统那样，CLOS不强制封装，任何槽都可以使用`slot-value`函数，或通过可选的自动生成的访问子方法来访问。要通过`slot-value`进行访问，就要知道这个槽的名字，CL编程者使用这个语言的包设施，来声明哪些函数或数据结构意图被导出。

CLOS允许多重继承。在多重继承中方法执行的缺省次序不正确的时候，编程者可以通过指定方法组合的次序，来解决这种菱形问题。在CLOS中已经解决了圆继承椭圆问题，而多数面向对象编程设计模式，要么消失，要么在性质上更加简单了。

CLOS是动态的，意味着不只是内容，它的对象的结构，也可以在运行时间修改。CLOS支持现场编码（on-the-fly）变更类定义，即使正在考虑的这个类的实例已经存在；还有通过`change-class`算子，变更一个给定实例的类成员关系。CLOS还允许在运行时间增加、重新定义和移除方法。

### 多分派

CLOS是一个多分派系统，这意味着方法，可以依据它们所要求的任何或所有实际参数来指定。多数面向对象语言是单分派的，意味着方法只能依据第一个实际参数来指定。方法不从属于类，类不为泛化函数或方法提供命名空间。方法是独立于类而定义的，并且它们对类的槽没有特殊的访问，CLOS不设立其他面向对象语言中常见的`this`、`self`或`protected`。

在CLOS中的方法被组织入泛化函数。泛化函数是像函数一样可调用的一个对象，它关联着有共享的名字和参数结构的方法的一个搜集，其中每个都特定于不同的实际参数。CLOS方法定义中的特定符（specializer），包容Common Lisp中内建数据类型的类型规定符（specifier），非CLOS类比如整数、实数、字符串、字符、符号、数组等，有类型规定符`integer`、`real`、`string`、`character`、`symbol`、`array`等，CLOS分派从而也工作在这些非CLOS类之上。CLOS还通过`eql`类型规定符支持分派在个体对象之上。特定符不能是类型规定符列表比如`(vector single-float)`。

多数Common Lisp实现，提供了元对象协议，它允许泛化函数提供特定于应用的特例（specialization）和分派规则。下面的泛化函数的例子采用了SBCL来演示：

```lisp
; 声明共同的实际参数结构原型
* (defgeneric f (x y))
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::F (0)>

; 定义对(f integer t)的实现，这里的t匹配所有类型
* (defmethod f ((x integer) y) 1)
#<STANDARD-METHOD COMMON-LISP-USER::F (INTEGER T) {10018B1F03}>

* (f 1 2.0)
1

; 定义对(f integer real)的实现
* (defmethod f ((x integer) (y real)) 2)
#<STANDARD-METHOD COMMON-LISP-USER::F (INTEGER REAL) {1001A0BB83}>

* (f 1 2.0) ; 分派发生在运行时间
2
```

#### 方法组合

<image>

在CLOS的多分派机制中，可以定义出一些辅助方法，即在定义方法时于方法名字之附加上限定符（qualifier）`:before`、`:after`和`:around`。不带有限定符的方法是主要方法。在CLOS的泛化函数的多分派机制中，方法选择与组合不同于多数面向对象语言：

- 给出一个实际参数列表，确定出可应用的那些方法，按限定符分别形成各自列表。
- 诸列表依据它们的形式参数的特定符（specializer）的特殊性（specificity）来排序。
- 接着使用泛化函数所用的那种方法组合，将从诸列表选择出的那些方法组合成一个有效方法。
- 接着用最初的实际参数调用这个有效方法。

标准方法组合形成的有效方法中诸方法的按如下阶段执行：

- 如果有`:around`方法，则最特殊的`:around`方法最先执行。如果`:around`方法调用了`call-next-method`函数，进而在类层级中有下一个可以应用的`:around`方法，则执行这个方法；如果`:around`方法没有调用了`call-next-method`函数，则终止后续执行阶段。
- 所有`:before`方法基于类层级按照越特殊越优先的次序依次执行。
- 最特殊的主要方法最先执行。如果主要方法调用了`call-next-method`函数，进而在类层级中有下一个可以应用的主要方法，则执行这个方法，否则通知发生了错误并中止后续执行阶段。
- 所有`:after`方法基于类层级按照越广泛越优先的次序依次执行。

这种分派机制工作在运行时间，增加或移除方法，将导致在运行时间变更有效方法，即使是在以相同实际参数调用泛化函数的时候都会变更，而变更方法组合还可导致不同的有效方法。标准方法组合提供上述的主要、之前、之后和围绕方法，还有具有其他方法类型的其他方法组合，也可以定义新的或简单或复杂的方法组合和方法类型。

标准方法组合中的这些辅助方法，对应于面向方面编程中的建议 (编程)。对比于在方面 (编程)中规定当其他的类的指定名称方法被执行时触发建议的定点切入，CLOS的泛化函数直接在类层级中选择同名方法并组合上它的辅助方法形成有效方法来主动执行。

## 元对象协议

在ANSI Common Lisp标准之外，有一个广泛实现的对CLOS的扩展，叫做元对象协议（MOP）。MOP定义到CLOS实现基础支撑的标准接口，将类、槽描述、泛化函数和方法自身，当作元类的实例，并允许定义新的元类，和修改所有CLOS行为。CLOS MOP的灵活性，预示了面向方面编程，它是由同一群工程师比如Gregor Kiczales开发的。

元对象协议通过一组协议，定义了整个对象系统的行为。这些定义是依据CLOS定义的，因此有可能通过扩展或变更已提供的CLOS系统的功能，来建立新的对象系统。1991年出版的图书《元对象协议的艺术》描述了CLOS MOP的使用和实现。各种Common Lisp实现对MOP有稍微不同的支持。Closer计划致力于提供缺失的特征。

## 来自更早的基于Lisp的对象系统的影响

Flavors (编程语言)和它的后继者New Flavors，是MIT Lisp机器上的对象系统。Lisp机器操作系统的很大部分和它的很多应用，使用了Flavors或New Flavors。Flavors介入了多重继承和mixin和其他一些特征。Flavors使用消息传递范型，New Flavors介入了泛化函数。Flavors尽管存在针对Common Lisp实现，现已几乎废止了。

CommonLoops，是Xerox Interlisp-D的LOOPS的后继者。CommonLoops是针对Common Lisp的实现。叫做可移植CommonLoops（PCL）的可移植实现，是第一个CLOS实现。PCL被广泛的移植了，并仍为很多Common Lisp实现提供CLOS实现的基础。PCL绝大部分是以可移植的Common Lisp实现的，而只有很少的系统依赖部分。

## 其他语言中的CLOS

由于CLOS的能力和表达力，还有历史上能获得到TinyCLOS，它是Gregor Kiczales为用于Scheme而书写的简化的可移植的CLOS实现，故而类CLOS的基于MOP的对象系统，成为大多数Lisp方言实现的事实规范：

- Tiny CLOS，Gregor Kiczales开发。
- Gauche。
- Chicken使用COOPS。
- GNU Guile使用GOOPS。
- MIT Scheme使用SOS。
- Sagittarius。
- STklos。
- Racket使用Swindle。
- Emacs Lisp使用EIEIO。
- ISLISP使用ILOS。

CLOS还能在一些其他语言的OOP设施中找到它：

- COS，C对象系统。
- Dylan
- Dynace，（很大程度上）CLOS的C实现。
- PyCLOS，为Python 3实现了CLOS风格的泛化函数。

## 参考文献

- Jo A. Lawless and Molly M. Miller, Understanding CLOS: the Common Lisp Object System, 1991, Digital Press,
- The Common Lisp Object System: An Overview by Richard P. Gabriel and Linda DeMichiel provides a good introduction to the motivation for defining classes by means of generic functions.
- Fundamentals of CLOS by Nick Levine provides a step-by-step exposure to the implementation of OO concepts in CLOS, and how to utilize them. It is intended for anybody with a basic knowledge of Lisp or Scheme.
- Common Lisp HyperSpec, Chapter 7: Objects

## 基本资料

- 日语原文：Common Lisp Object System
- 编程范型：面向对象
- 开发者：ANSI X3J13委员会
- 发行日期：1988年
- 软件版本周期：从属于ANSI Common Lisp（1994年）
- 实作语言：Common Lisp
- 操作系统：跨平台
- 受影响于：MIT Flavors (编程语言), Xerox CommonLoops
- 施影响于：Dylan, Tiny CLOS, COOPS, STklos, Gauche
- 网站： Common Lisp HyperSpec, Chapter 7: Objects