Smalltalk语言

**Smalltalk** 是一種動態型別、反射式的物件導向程式語言。Smalltalk 由艾倫・凱、Dan Ingalls、Ted Kaehler、Adele Goldberg 等於 1970 年代在全錄帕羅奧多研究中心開始開發。

Smalltalk 對其它眾多的程式設計語言的產生起到了極大的推動作用，特別是 Objective-C、CLOS、Python 和 Ruby 等。1990 年代湧現的許多軟體開發思想都得益於 Smalltalk，例如設計模式、敏捷編程和代碼重構等。

## 概述

Smalltalk 和許多程式設計語言不同，它不僅僅是一門語言。下面從幾個不同的角度來解釋 Smalltalk。

* 一種物件導向的程式設計語言：它是一種物件導向的語言，包含語言的語法和語意。一些編譯器可以透過 Smalltalk 源程式產生可執行檔。這些編譯器通常產生一種能在虛擬機器上執行的二進位代碼。Smalltalk 語言本身非常精煉。
* 一種程式設計環境：這裡指的是一種提供許多物件的系統，而不是某種特殊的開發環境。和許多語言不同（包括 C++），Smalltalk 附帶有一個巨大的、相當標準的類別館。這些類使得開發 Smalltalk 程式的效率非常高。在其它語言（例如 Ada、C 和 Pascal）中，通常被作為語言的一部分的功能（例如條件判斷，迴圈等），在 Smalltalk 由特定的類提供。
* 一個應用開發環境（ADE）：由於 Smalltalk 的歷史原因，它具有一個非常優秀的高度整合、開放的應用開發環境。由於開發環境中的瀏覽器、監視器以及除錯器，都由同樣的源程式衍生出來的，不同的版本之間也具有相當好的相容性。此外，這些工具的源程式都可以在 ADE 直接存取。

## 歷史

最早的 Smalltalk 原型由艾倫・凱於 1970 年代初提出。類（來自 Simula 67）、海龜繪圖（來自 LOGO）以及圖形化使用者介面（來自 Sketchpad 等先驅系統）等概念的有機組合，構成了 Smalltalk 的最初的藍圖。

在 1971 年到 1975 年之間，艾倫・凱在 Xerox PARC 的小組，在 Xerox Alto 電腦上，設計並實現了第一個真正的 Smalltalk 語言系統，編譯器由 Dan Ingalls 負責主要實作。這個系統被稱為 Smalltalk-71 與 Smalltalk-72，具有以下幾個技術創新：

* 語言完全基於訊息交換和 Simula 67 的類的概念。
* 語言沒有固定的語法，語法分析由類本身完成。

開發環境的革新相當迅速。雖然當時的點陣圖顯示器十分昂貴，但是艾倫・凱卻說服了 PARC，讓他使用這些點陣圖顯示器，這使得艾倫・凱和他的小組，能夠實現不同大小和字型的文字，使用多窗口環境，以及一些對圖像處理的高端支援。Smalltalk-72 影響了演員模型的發展，它的語法和執行模型，與現代的 Smalltalk 變體有著顯著的差異。

在 1975 到 1976 年間，艾倫・凱小組認識到應當對執行效率和規模進行最佳化。於是他們在許多重要方面重新設計了 Smalltalk 系統，被稱為 Smalltalk-76，它在語言上：

* 引入了繼承和子類的概念。
* 確定了語言的語法，這使得編譯器能夠產生高效、可執行、精煉的二進位代碼。
* 拉里・泰斯勒設計了類瀏覽器，這極大地提高了 Smalltalk 程式設計師的編程效率。

前述的所有 Smalltalk 系統，都是在特殊的硬體上實現的，直到 1977 年至 1978 年，Bruce Horn 和 Ted Kaehler 把 Smalltalk-76 移植到 Xerox NoteTaker 上，它是由 Intel 8086 處理器和自訂顯示器所組成的硬體環境。雖然這種硬體環境只生產了 10 台，但是它證明了在通常的處理器上實現 Smalltalk 的可能性。

在 1979 至 1980 年，部分受 NoteTaker 專案的影響，Smalltalk 小組的注意力轉移到 Smalltalk 的銷售可行性上。小組設計並實現了新一代的 Smalltalk 系統，這次修改的目標著重於在標準硬體上的移植性等方面，被稱為 Smalltalk-80，它包括：

* 採取 ASCII 碼字元集，摒棄了原先在 Smalltalk-72 和 Smalltalk-76 中使用的特殊字元。
* 取消了原始方法直接存取記憶體的能力。取而代之的是引入一系列的原始方法提供相應的功能。
* 引入了元類的概念。
* 引入 MVC（模型 - 視圖 - 控制器）系統以方便互動式應用軟體的開發。

Smalltalk-80 是在 PARC 之外能獲得到的第一個語言變體，最初作為 Smalltalk-80 版本 1，給與了少數公司（惠普、蘋果公司、泰克和 DEC）和大學（UC Berkeley），用於同行評審和在它們自己的平台上實現。後來在 1983 年普遍可獲得的實現，叫做 Smalltalk-80 版本 2，發行為虛擬機器規定和映像（具有物件定義的獨立於平台的檔案）。

1988 年 Xerox PARC 為了將 Smalltalk 推向市場而成立了分拆公司 ParcPlace Systems。ANSI Smalltalk 自從 1998 年來是標準的語言參考。

兩個當前流行的 Smalltalk 實現變體，是這些最初 Smalltalk-80 映像的後代。Squeak 是開源實現，它經由 Apple Smalltalk，衍生自 Smalltalk-80 版本 1.03。VisualWorks 經由 Smalltalk-80 2.5 和 ObjectWorks（二者都是 ParcPlace Systems 的產品），衍生自 Smalltalk-80 版本 2。

## 物件導向程式設計

如同其他物件導向語言，Smalltalk-80（而非 Smalltalk-72）的中心概念是「物件」 。一個物件總是一個「類」的一個「實例」。類是描述它們的實例的屬性和行為的「藍圖」。例如，一個 GUI 窗口類，可以聲明窗口擁有的屬性，比如標籤、位置和窗口是否可見。這個類還可以聲明其實例支援的操作，比如打開、關閉、移動和隱藏。每個特定窗口物件，對這些屬性都有自己的值，它們每個都能進行它的類別定義的操作。

Smalltalk 物件確切的可以做三件事：

1. 持有狀態（參照到其他物件）。
2. 接收訊息自本身或其他物件。
3. 在處理一個訊息的過程中，傳送訊息至本身或其他物件。

一個物件持有的狀態總是私有於這個物件。其他物件只能通過發動請求（訊息）至這個物件，來讓它做出查詢或變更這個狀態。任何訊息可以傳送給任何物件：當接收到一個訊息的時候，接收者確定這個訊息是否合適。Alan Kay 評論說，儘管關注於物件，訊息才是 Smalltalk 中最重要的概念：「最大的想法是訊息傳遞，它是 Smalltalk/Squeak 核心的全部意義所在（它是我們在 Xerox PARC 階段從未真正完成的某種東西）。」

不同於多數其他語言，Smalltalk 物件可以在系統執行的同時進行修改。現場編碼和飛速應用修補程式，是 Smalltalk 的主導編程方法論，並且是它高效的主要原因。

Smalltalk 是「純」物件導向程式設計語言，這意味著，不像 C++ 和 Java，在作為物件的值和作為原始類型的值之間沒有區別。在 Smalltalk 中，原始值比如整數、布林值和字元，也是物件，這麼說的意義在於它們也是相應類別的實例，而且要傳送訊息來呼叫在它們上的運算。編程者可以通過子類，改變或擴充實現原始值的類，使得可以向它們的實例定義新行為，例如實現一個新的控制結構，甚至使得它們現有行為得以改變。這個事實被總結成常聽到的一句短語：「在 Smalltalk 中，所有東西都是物件」，它可以更精確的表達為：「所有的值都是物件」，因為變數不是。

因為所有的值都是物件，類也是物件。每個類都是這個類的元類的一個實例。元類都是 `Metaclass`（元類類）的實例，它也是物件，並且是 `Metaclass class`（元類元類）的實例。代碼塊是 Smalltalk 表達匿名函式的方式，它也是物件。

## Hello, World! 例子

Hello, World! 程式，實質上被所有電腦語言的課本用作要學習的第一個程式，它展示了這個語言的最基本語法和環境。對於 Smalltalk，這個程式可極其簡單的書寫。下列代碼中，訊息 `show:` 被傳送給物件 `Transcript`，具有字串文字 `'Hello, World!'` 作為它的實際參數。呼叫 `show:` 方法，導致它的實際參數，即字串文字 `'Hello, World!'`，顯示在叫做「副本」（Transcript）的終端窗口：

注意需要打開 Transcript 窗口，來看到這個例子的結果。

## 語法

Smalltalk-80 語法是相當極簡主義的，只基於了一小把的聲明和保留字。事實上，Smalltalk 中只保留了六個「關鍵字」：`true`、`false`、`nil`、`self`、`super` 和 `thisContext`。它們的準確術語是「偽變數」，是服從變數識別碼規則的識別碼，但指示了編程者所不能變更的繫結。`true`、`false` 和 `nil` 偽變數是單例實例。`self` 和 `super`，在回應一個訊息而啟用的方法中，指稱這個訊息的接收者；但是傳送給 `super` 的訊息，在這個方法的定義類的超類中尋找方法，而非這個接收者的類中，這允許子類中的方法呼叫在超類中的同名方法。`thisContext` 指稱當前的活動記錄。

內建的語言構造只有訊息傳送、賦值、方法返回和某些物件的文字語法。從它最初作為給所有年齡兒童的語言開始，標準的 Smalltalk 語法以更像英語，而非主流編碼語言的方式使用標點符號。語言餘下部份，包括用於條件求值和迭代的控制結構，都由標準 Smalltalk 類別館實現在內建構造之上。出於效能上的原因，實現可以辨識並特殊處理某些這種訊息，但這只是最佳化而並未硬性規定入語言語法。

諺語「Smalltalk 語法適合一張明信片」，所提及的是 Ralph Johnson 的一個代碼片段，展示了一個方法的所有基本標準語法元素：

### 文字

下列例子詮釋了最常用的物件，可以在 Smalltalk-80 方法中被寫為文字值。

#### 數和字元

下列是數的某些可能例子：

最後兩個專案分別是二進位和十六進位數。在 `r` 前的數是底數或基數。基數不必須是二的冪；例如 `36rSMALLTALK` 是一個有效的數值，等價於十進位的 `80738163270632`。

字元書寫時帶有前導的美元符：

#### 字串

字串是包圍在單引號內的字元序列：

要在一個字串中包括一個引號，使用另一個引號來跳脫：

雙引號不需要跳脫，因為單引號界定字串：

兩個相等的字串（字串相等，如果它們包含完全相同的字元）可以是駐留在記憶體不同位置中的不同物件。

#### 符號

除了字串，Smalltalk 有一類叫做符號（`Symbol`）的字元序列物件。符號保證是唯一的，沒有作為不同物件的兩個相等的符號。因此，符號非常易於比較，並經常用於語言構造中，比如用作訊息選擇子。

符號被寫為 `#` 跟隨著字串文字。比如：

如果一個序列不包含空白或標點字元，還可以寫為：

#### 陣列

例如定義了四個整數的一個陣列：

很多實現支援下列位元組陣列（`ByteArray`）的文字語法，例如定義了四個整數的位元組陣列：

#### 其他

最後卻重要的是塊（匿名函式文字）:

很多 Smalltalk 方言為其他物件實現了額外的語法，但是上述的是所有方言都本質上支援的。

### 變數聲明

在各種 Smalltalk 中共同使用的有兩種變數：實例變數和臨時變數。其他變數和有關術語依賴於特定實現，例如 VisualWorks 有類共享變數和命名空間共享變數，而 Squeak 和很多其他實現，有類別變數、池變數和全域變數。

在 Smalltalk 中臨時變數聲明是在方法（見後）內聲明的變數。它們聲明在方法的頂部，作為由豎槓包圍的空格分隔的名字。例如：

聲明一個臨時變數名叫 `index`，可以包含初始值 `nil`。

多個變數可以在一組豎槓內聲明：

聲明了兩個變數：`index` 和 `vowels`。所有變數都要初始化。字串的索引變數，初始化為 `null` 字元或初始為 `0` 的 `ByteArray`，此外的所有變數初始化為 `nil`。

按命名約定，實例變數、臨時變數、方法或塊的參數，應當以小寫字母開頭，指示它們具有私有作用域，它們合稱為局部變數。而全域變數、類別變數、池字典、類名字，應當以大寫字母開頭，它們合稱為共享變數。

### 賦值

變數通過 `:=` 語法來指定一個值。比如：

指定字串 `'aeiou'` 至前面聲明的 `vowels` 變數。這個字串是個物件（在單引號之間的字元序列是文字字串的語法），在編譯時間由編譯器建立。

在最初的 Parc Place 映像中，現在底線（`_`）的字形，在那時是左向箭頭（`←`）字形（就像 1963 年版本 ASCII 代碼中那樣）。Smalltalk 最初接受左向箭頭，作為唯一的賦值算符。一些現代代碼仍然包含充當賦值的底線，會讓人想起這種最初的用法。多數現代的 Smalltalk 實現接受要麼底線，要麼冒號等號語法。

### 訊息

訊息是 Smalltalk 中最基礎的語言構造。所有控制結構都實現為訊息傳送。Smalltalk 預設的採用動態分派和單一分派策略，這是相對於其他一些物件導向語言使用的多分派而言的。

#### 一元訊息

下列例子是傳送訊息 `factorial` 至數值 `42`：

在這種情況下，`42` 叫做這個訊息的「接收者」，而 `factorial` 是訊息的選擇子。接收者通過返回一個值來相應這個訊息（這個情形中是 `42` 的階乘）。同其他事物一樣，訊息的結果可以賦值給一個變數：

上面的 `factorial` 是「一元」訊息，因為只涉及了一個物件，即接收者。

#### 關鍵字訊息

訊息可以承載額外的物件作為實際參數，比如：

在這個表達式中，涉及了兩個變數：`2` 作為接收者而 `4` 作為訊息的實際參數。訊息結果，或用 Smalltalk 的說法，回答被認定為 `16`。這種訊息叫做「關鍵字」訊息。訊息可以有多個實際參數，使用如下語法:

它的回答是在接收者字串中字元 `o` 的索引，從索引 `6` 開始尋找。這個訊息的選擇子是 `indexOf:startingAt:`，構成自兩個部份或關鍵字。

這種關鍵字和實際參數的交織意圖改進代碼的可讀性，因為實際參數由前導於它們的關鍵字來解釋。例如，要建立一個矩形的表達式使用 C++ 或 Java 類別語法可以寫為：

不清楚這些實際參數分別是什麼。與之相反，在 Smalltalk 中，這個代碼可以寫為：

這個情況下接收者是 `Rectangle` 類，回答是這個類的具有指定寬度和高度的一個實例。

#### 二元訊息

最後，多數特殊（非字母）字元可以被用作所謂的「二元訊息」。這些允許了數學和邏輯算符以傳統形式書寫：

它傳送訊息 `+` 給接收者 `3`，具有 `4` 作為實際參數傳遞（回答將是 `7`）。類似的：

將訊息 `>` 傳送給 `3` 具有實際參數 `4`（回答將是 `false`）。

注意，Smalltalk-80 語言自身，不包含著這些算符的含義。上述的結果，都只是這些訊息的接收者（這裡是數值實例），為了回應訊息 `+` 和 `>` 而定義並返回的。這個機制的副作用是運算子多載，訊息 `>` 可以被其他物件所理解，允許使用形如 `a > b` 的表達式來比較它們。

### 表達式

一元訊息可以一個接一個的寫成方法鏈式呼叫：

它傳送 `factorial` 到 `3`，接著傳送 `factorial` 到前面的結果 `6`，接著傳送 `log` 到前面的結果 `720`，產生最終的結果 `2.85733`。

一個表達式可以包括多次訊息傳送。在這個情況下，表達式依據一個簡單的優先級次序來分析。一元訊息有最高的優先級，隨後是二元訊息，最後是關鍵字訊息。例如：

被求值如下：

1. `3` 接收訊息 `factorial` 並回答 `6`
2. `4` 接收訊息 `factorial` 並回答 `24`
3. `6` 接收訊息 `+` 具有 `24` 作為實際參數並回答 `30`
4. `30` 接收訊息 `between:and:` 具有 `10` 和 `100` 作為實際參數並回答 `true`

最後的訊息傳送的回答，是整個表達式的結果。

#### 組合

在需要的時候使用圓括號可以改變求值的次序。例如：

將改變表達式含義，首先計算 `3 factorial + 4` 產生 `10`。接著 `10` 接收第二個 `factorial` 訊息，產生 `3628800`。`3628800` 接著接收 `between:and:`，回答 `false`。

注意由於二元訊息的含義，不是硬性規定入 Smalltalk-80 語法的，它們全部都被認為有相等的優先級，並簡單的從左至右來求值。因此，使用二元訊息的 Smalltalk 表達式的含義，可能不同於傳統釋義：

被求值為 `(3 + 4) * 5`，產生 `35`。要得到預期回答 `23`，必須使用圓括號來顯式的定義運算次序：

#### 複合

以點號分隔的表示式按順序執行。注意在變數定義和隨後的表達式之間沒有點號。一個表達式序列的值，是最後的表達式的值。除了最後的表達式之外，所有的表達式的值都被忽略。注意點號是分隔符而並非終結符，因此最終的點號是可選的。

下列（假想）例子中，書寫了一序列的表達式，每個都用點號分隔。這個例子首先建立類 `Window` 的一個新實例，儲存它在一個變數中，接著向它傳送兩個訊息：

#### 級聯

如果像上述例子這樣，將一序列訊息都傳送給相同的接收者，它們也可以寫為方法級聯呼叫，具有用分號分隔的單獨訊息：

這種將前面例子的重新為一個單一表達式，避免了對將新窗口儲存在臨時變數的需要。依據平常的優先級規則，首先向 `Window` 類傳送一元訊息 `new`，接著向 `new` 回答的那個物件，傳送 `label:` 和 `open`。

可以使用 `yourself` 訊息來返回一個級聯訊息的接收者。

### 代碼塊

塊是頭等物件。代碼塊即匿名函式，可以被表達一個文字值（它是一個物件，因為所有值都是物件）。這是通過方括號達成的：

這裡的 `:params` 是代碼可以接受的形式參數的列表。結果的塊物件可以形成一個閉包：它可以在任何時間存取它外圍的詞法作用域內的變數。這意味著下列 Smalltalk 代碼：

可以理解為： $f(x)=x+1$ ，或用 λ 演算表達為： $\lambda x$  : $x+1$ 。

塊可以通過傳送給它們 `value` 訊息來執行。塊有一個參數用 `value:`，有 2 個參數使用 `value:value:`，以此類推直到 4 個參數，對多於 4 個參數使用 `valueWithArguments:` 並將參數作為陣列傳遞。例如下面的表達式：

可以被求值為： $f(3)=3+1$ ，或用 λ 演算表達為： $(\lambda x.x+1)\,3\,{\underset {\beta }{\rightarrow }}\,3+1$ 。

塊返回（常稱為回答）其主體的最後一個表達式的值，除非有一個由顯式的 `^` 指示的返回，這時返回這個返回表達式的值。在塊內部的返回，充當了一種逃出（escape）機制。在一個巢狀的塊表達中的返回表達式，將終止在字面上包圍的方法。

塊的文字表示是一種創新，它一方面允許特定代碼有更重大的可讀性；它允許涉及迭代的演算法一更清晰和簡潔的方式編碼。典型的在某些語言中使用迴圈寫成的代碼，可以在 Smalltalk 中使用塊簡潔的書寫，有時在單一一行之內。更加重要的，塊允許使用訊息和多型來表達控制結構，因為塊推延了計算，而多型可以用來選擇交替者（alternative）。所以在 Smalltalk 80 中，`if…then…else` 被書寫和實現為：

再舉一例，向一個搜集傳送訊息 `select:`：

注意這與函式語言程式設計有關，這裡的計算模式被抽象成了高階函式。`select:` 等價於在一個適當的函子上的高階函式 filter。

## 控制結構

在 Smalltalk 中控制結構沒有特殊的語法。它們轉而實現為傳送到物件上的訊息。以條件執行為例，布林類 `Boolean` 定義了 `ifTrue:`、`ifFalse:`、`ifTrue:ifFalse:` 和 `ifFalse:ifTrue:` 方法。比如向一個布林物件，傳送 `ifTrue:` 訊息，並傳遞一個代碼塊作為實際參數，這個塊被執行若且唯若布林接收者為真。下面用一個例子來展示：

塊也被用來實現，使用者定義控制結構、列舉元、存取者、例外處理、可插拔的行為和很多其他模式。

### 迭代

下面例子，從一個字串中過濾出其中所含有的元音字元：

在最後一行，向字串物件 `aString` 傳送一個 `select:` 訊息，它具有一個代碼塊 `[:aCharacter | aCharacter isVowel]` 作為實際參數。這個代碼塊，表示一個測試，代碼塊文字將被用作一個謂詞函式，它回答 `true`，若且唯若這個字串的一個元素 `aCharacter`，應當被包括在滿足這個測試的字元搜集之中。

字串類 `String` 回應 `select:` 訊息，要呼叫的 `select:` 方法，定義並實現在搜集類 `Collection` 中；它將給 `select:` 的實際參數選擇塊，傳送給形式參數 `aBlock`；然後將繫結了選擇塊的 `aBlock` 嵌入到迭代塊的代碼之中，再把這個迭代塊作為向字串自身傳送的 `do:` 訊息的實際參數，從而將這個字串所包含的每個字元，都作為實際參數傳送給這個迭代塊，而各做一次求值。在求值迭代塊的時候，通過 `value:` 訊息，將迭代元素傳送給 `aBlock` 所繫結的選擇塊，它回答一個布林值；接著向它傳送 `ifTrue:` 訊息，如果這個布林值是物件 `true`，則將這個字元增加到要返回的字串中。

字串類 `String` 回應 `do:` 訊息，要呼叫的 `do:` 方法，定義在可迭代類 `Iterable` 中，而實現在可序列化搜集類 `SequenceableCollection` 中，這個類是 `Iterable` 類的子類和 `String` 類的超類。

### 例外處理

Smalltalk 的例外處理機制，`Exception` 類及其子類比如 `Error` 類，類似於 CLOS 的例外處理樣式，使用塊作為處理器：

例外處理器的 `ex` 實際參數，提供對掛起運算的狀態的存取，比如它的堆疊框、行號、接收者和實際參數等，並且通過傳送 `ex proceed`、`ex reject`、`ex restart` 或 `ex return` 之一，還可用來控制計算怎樣繼續。

## 類

類通過實例變數定義它的實例的結構，通過方法定義它的實例的行為。每個方法都有叫做選擇子的一個名字，它在這個類之內是唯一性的。

### 定義

下面是個平凡的類別定義：

多數這種定義經常由編程環境來填充。這裡的類別定義是給 `Object` 類的一個訊息，用來建立它叫做 `MessagePublisher` 的一個子類。

在 Smalltalk 中類是頭等物件，它可以就像任何其他物件一樣接收訊息，並可以在執行時間動態的建立。`Object` 類在收到這個 `subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:` 訊息之時，原則上首先在其元類 `Object class` 中尋找對應方法，未果而上溯繼承鏈在其超類 `Class` 類中找到對應方法實現。

在 Smalltalk 中，實例變數是這個實例的私有變數，它可以在定義它們的類的任何實例方法中，還有在它的子類中定義的方法中，通過名字來存取。在需要於一個類的所有實例、這個類本身和它的子類之間，共享某個資料的時候，要使用類別變數，它是由這個類和它的所有實例共享的私有變數。池變數是在可以沒有繼承關聯的多個類之間共享的變數。池變數最初儲存在池字典中，現在它們應當被定義為專門的類（`SharedPool` 的子類）的類別變數。

`category` 指示有關的類的「群組」，在現代 Smalltalk 版本如 Pharo 中被替代為 `package`，包系統是利用簡單的命名約定，組織 Squeak 和 Pharo 原始碼的簡單而輕量級的方式。

在一個類所對應的元類中定義的實例變數叫做類別實例變數，每個類都有自己私有的類別實例變數，子類將繼承這些類別實例變數，但是子類會擁有這些變數的它們自己的私有複本，類和它們的子類不分享類別實例變數。例如，可以定義一個類別實例變數叫做 `count` 來跟蹤一個給定的類有多少實例。

類不能直接存取它的實例的實例變數，而實例不能存取它們的類的類別實例變數。如果需要的話必須定義變異子與存取子。

### 方法

當一個物件接收到一個訊息的時候，呼叫匹配這個訊息名字的一個方法。所有方法都是公開的和虛擬的（也就是動態尋找的）。方法被分組入指示它們意圖的協定（protocol）之中。

為一個類增加方法涉及到 `Behavior` 類，其中的 `compile:` 方法，編譯一個方法原始碼並返回一個 `CompiledMethod` 類別的實例；`addSelector:withMethod:` 方法，將給定的一個編譯過的方法增加到方法字典中。在 `Behavior` 類的子類 `ClassDescription` 類中，`compile:classified:` 方法，編譯一個方法的原始碼，並為這個方法指派上給定的歸類（對於方法稱為協定）。

物件負責在執行時間動態的確定，執行哪個方法來回應一個訊息，儘管在很多語言中，這可能是（有時或總是）在編譯時間靜態確定的。下列代碼定義一個方法 `publish`，並且這個定義將在這個物件收到 `publish` 訊息的時候發生。

下列名字為 `#quadMultiply:and:` 的方法，演示了接收多個實際參數並返回一個值：

執行任何前導了 `^`（脫字元或 `↑`）的表達式，都導致這個方法於這一點登出，並返回這個表達式的值。終止而沒有顯式返回某個表達式的一個方法，將隱含的返回自身。

### 實例化

為一個類新建一個實例，要用到 `new` 方法，它定義在 `Behavior` 類中。下列例子代碼：

建立並返回 `MessagePublisher` 類的一個新實例。它典型的會被賦值到一個變數：

但是也可以向一個臨時的匿名物件傳送一個訊息：

### 類別方法

類別方法就是在一個類的元類中定義的方法。

比如搜集類 `Collection` 的類別方法中，實例建立方法有 `with:`、`with:with:`、一直到 `with:with:with:with:with:with:` 方法。在下面的例子中，將 `with:with:` 用於有序搜集類 `OrderedCollection`，它是 `SequenceableCollection` 類的子類，故而能最終上溯至超類搜集類：

## 反射

反射是一個電腦科學術語，適用於有能力檢查它們自己的結構的軟體程式，例如檢查它們的分析樹或輸入和輸出參數的資料類型。反射是動態、互動式語言比如 Smalltalk 和 Lisp 的一個特徵。具有反射的互動式程式（要麼解釋的要麼編譯的）維護所有記憶體內物件的狀態，包括代碼物件自身，這是在解析 / 編譯期間生成的，並且是在編程上可存取和修改的。

反射也是 Smalltalk 這種有元模型的語言的一個特徵。元模型是描述這個語言的模型，開發者可以使用元模型來做事，比如遊歷、檢查和修改一個物件的分析樹，或找到特定種類的結構的所有實例（例如在元模型中 `Method` 類的所有實例）。

Smalltalk-80 是完全的反射式系統，用 Smalltalk-80 語言實現。Smalltalk-80 提供了結構性和計算性反射二者。Smalltalk 是結構性反射式系統，其結構是由 Smalltalk-80 物件定義的。定義這個系統的類和方法也是物件，並且完全是它們所有助力定義的系統的一部份。Smalltalk 編譯器將文字原始碼編譯成方法物件，典型是的 `CompiledMethod` 的實例。通過把它們儲存入一個類的方法字典，而增加到這個類。類層級的定義類的那部份，可以向系統增加新類。這個系統是通過執行建立或定義類和方法的 Smalltalk-80 代碼來擴充的。Smalltalk-80 系統是個現場（living）系統，承載著在執行時間擴充自身的能力。

因為類是物件，可以向它們提問比如：「你實現了哪些方法？」或「你定義了什麼欄位 / 槽 / 實例變數？」。所以通過能應用於系統中的任何物件的普通的代碼，物件可以輕易的檢查、複製、（去）序列化，諸如此類。

Smalltalk-80 還提供計算性反射，有能力觀察系統的計算狀態。在衍生自最初 Smalltalk-80 的語言中，一個方法的當前活動（activation），可以作為通過偽變數命名的一個物件來存取，這個偽變數是作為六個保留字之一的 `thisContext`。通過傳送訊息至 `thisContext`，一個方法活動可以提問比如：「誰給你傳送了這個訊息？」。這些設施使得有可能實現協程，或類似 Prolog 的回溯，而不需要修改虛擬機器。異常系統也是使用這個設施實現的。這個設施更有趣的用法之一，是在 Seaside web 框架之中，它通過為每個編輯的頁面儲存續體，並在它們之間切換來導航一個 web 站點，緩解了編程者處理 Web 瀏覽器的返回按鈕的複雜性。使用 Seaside 編程 web 伺服器，可以使用更常規的編程風格來完成。

Smalltalk 如何使用反射的一個例子，是處理錯誤的機制。當一個物件被傳送了一個它沒有實現的訊息的時候，虛擬機器傳送給這個物件 `doesNotUnderstand:` 訊息，具有這個訊息的實化作為實際參數。這個訊息（它是另一個物件，是 `Message` 的實例），包含這個訊息的選擇子和它的實際參數的一個 `Array`。在互動式 Smalltalk 系統中，`doesNotUnderstand:` 的預設實現，是打開一個錯誤窗口（一個 `Notifier`）向使用者報告錯誤。通過它和反射設施，使用者可以檢查錯誤在其中發生的上下文，重新定義犯錯的代碼，並繼續，這一切都在這個系統之中，使用 Smalltalk-80 的反射設施。

通過建立只理解（實現）`doesNotUnderstand:` 的一個類，可以建立一個實例，經由它的 `doesNotUnderstand:` 方法能攔截傳送給它的任何訊息。這種實例可以叫做透明代理（proxy）。可以使用這種代理來實現很多設施，比如分散式 Smalltalk，這裡的訊息在多個 Smalltalk 系統之間交換，和資料庫介面，這裡的物件透明的從資料庫中排除錯誤，還有 promise 等。分散式 Smalltalk 的設計影響了如 CORBA 這樣的系統。

## 基於映像的持久儲存

多數流行的編程系統，將靜態的程式碼（以類別定義、函式或過程的形式），分離於動態的或執行時間的程式狀態（比如物件或其他形式的程式資料）。它們在程式啟動的時候裝載程式碼，而任何先前的程式狀態必須從設定檔或其他資料來源顯式的重新建立。程式（和編程者）未顯式儲存的設定，在每次重新啟動時都必須再次設立。傳統的程式在每次程式儲存一個檔案、登出和多載的時候，還失去很多有用的文件資訊。這會失去細節比如回退歷史或游標位置。基於映像的系統不會因為電腦關閉或 OS 更新，而強制失去所有這些東西。

但是很多 Smalltalk 系統，不區分程式資料（物件）和代碼（類）。事實上，類也是物件。因此，多數 Smalltalk 系統，儲存整個程式狀態（包括類和非類物件二者）在一個映像檔案之中。這個映像可以接著由 Smalltalk 虛擬機器裝載，將類 Smalltalk 系統恢復成先前的狀態。這是受到了 FLEX 的啟發，它是 Alan Kay 建立的語言並描述於他的科學碩士畢業論文中。

Smalltalk 映像類似於（可重新啟動的）核心轉儲，並可以提供與核心轉儲相同的功能，比如延遲或遠端除錯，具有對出錯時刻的程式狀態的完全存取。將應用代碼建模為某種形式的資料的其他語言比如 Lisp，也經常使用基於映像的持久儲存。這種持久儲存的方法，對於快速開發是強力的，因為所有開發資訊（比如程式的解析樹），都儲存而利用於除錯。但是它作為一個真實的持久儲存機制，也有一個嚴重的缺點。首先，開發者可能經常想要隱藏實現細節，並使它們在執行時間不可獲得。出於法律和維護的原因，允許任何人在執行時間修改程式，對於在執行時間環境不暴露原始碼的編譯後的系統，不可避免的介入複雜性和潛在的錯誤。其次，儘管持久儲存機制易於使用，它缺乏多數多使用者系統需要的真正持久儲存能力。最明顯的是進行同多個使用者並列存取相同的資料庫的事務。

## 實現列表

### OpenSmaltalk

OpenSmaltalk VM（OS VM）是 Smalltalk 執行時環境的著名實現，很多現代 Smalltalk VM 基於或衍生自它。OS VM 自身是從一組 Smalltalk 原始碼檔案（它們叫做 VMMaker），轉譯成原生 C 語言原始碼（通過使用叫做 Slang 的轉譯器），它依次再針對特定平台和硬體架構來編譯，實際上確使 Smalltalk 映像的跨平台執行。原始碼可以在 GitHub 上獲得並在 MIT 許可證下發布。OS VM 的知名衍生者有：

### JavaScript VM

### 其他

## 參見

* Seaside
* GLASS

## 參照

## 延伸閱讀

* Adele Goldberg; David Robson. Smalltalk-80: The Language and its Implementation (PDF). Addison-Wesley. May 1983 [2022-02-16]. ISBN0-201-11371-6. （原始內容 (PDF) 存檔於 2017-08-07）.
* Adele Goldberg. Smalltalk-80: The Interactive Programming Environment. Addison-Wesley. December 1983 [2022-02-09]. ISBN0-201-11372-4. （原始內容存檔於 2022-02-09）.
* Glen Krasner (編). Smalltalk-80: Bits of History, Words of Advice. Addison-Wesley. August 1983 [2022-02-09]. ISBN0-201-11669-3. （原始內容存檔於 2022-02-09）.
* Adele Goldberg; David Robson. Smalltalk 80: The Language. Addison-Wesley. 11 January 1989. ISBN 0-201-13688-0.
* Dan Ingalls. The Smalltalk-76 programming system design and implementation (PDF). Xerox Palo Alto Research Center. January 1978 [2022-03-12]. （原始內容 (PDF) 存檔於 2021-11-21）.
* Learning Research Group. How To Use the Smalltalk-76 System (PDF) (Report). Xerox Palo Alto Research Center. October 1979 [2022-03-12]. （原始內容 (PDF) 存檔於 2022-04-12）.
* Adele Goldberg; Alan Kay (編). Smalltalk-72 Instruction Manual (PDF). Xerox Palo Alto Research Center. March 1976 [2021-03-06]. （原始內容 (PDF) 存檔於 2020-11-12）.
* Alan Kay. The Early History of Smalltalk (PDF). ACM SIGPLAN Notices (ACM). March 1993, **28** (3): 69–95 [2021-03-06]. doi:10.1145/155360.155364. （原始內容 (PDF) 存檔於 2021-03-23）.
* Nierstrasz, Oscar; Ducasse, Stéphane; Pollet, Damien; Black, Andrew P. Squeak by Example. Kehrsatz, Switzerland: Square Bracket Associates. 2009-10-07 [2022-03-12]. ISBN978-3-9523341-0-2. （原始內容存檔於 2022-03-18）.
* Nierstrasz, Oscar; Ducasse, Stéphane; Pollet, Damien; Black, Andrew P. Pharo by Example. Kehrsatz, Switzerland: Square Bracket Publishing. February 23, 2010 [2022-03-12]. ISBN978-3-9523341-4-0. （原始內容存檔於 2022-02-10）.
* Special Issue on Smalltalk. Byte (McGraw-Hill). August 1981, **6** (8) [2013-10-18]. 
  * Adele Goldberg. Introducing the Smalltalk-80 System. Byte (McGraw-Hill). August 1981, **6** (8) [2013-10-18].
  * Dan Ingalls. Design Principles Behind Smalltalk. Byte (McGraw-Hill). August 1981, **6** (8) [2011-11-11].
  * Larry Tesler. The Smalltalk Environment. Byte (McGraw-Hill). August 1981, **6** (8) [2016-05-02].

## 外部連結

* Free Online Smalltalk Books （頁面存檔備份，存於網際網路檔案館）
* Smalltalk Zoo （頁面存檔備份，存於網際網路檔案館）
* Smalltalk-80 Bluebook implementations in C++: by dbanay （頁面存檔備份，存於網際網路檔案館） and rochus-keller （頁面存檔備份，存於網際網路檔案館） on github
* A minimal Smalltalk for teaching of and research on Virtual Machines. [2022-02-09]. （原始內容存檔於 2022-03-28）.