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