[go: up one dir, main page]

跳至內容

Smalltalk

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書
Smalltalk
編程範型面向對象反射式
設計者Alan KayDan Ingalls英語Dan IngallsAdele Goldberg英語Adele Goldberg (computer scientist)
實作者Alan KayDan Ingalls英語Dan IngallsAdele Goldberg英語Adele Goldberg (computer scientist)Ted Kaehler英語Ted KaehlerDiana Merry英語Diana Merry、Scott Wallace、Peter DeutschXerox PARC其他人
面市時間1972年,​52年前​(1972(開發始於1969年)
當前版本
  • ANSI Smalltalk(1998年5月19日)[1]
編輯維基數據鏈接
型態系統動態
作用域詞法(靜態)
系統平台Xerox Alto[2][3]
操作系統跨平台
主要實作產品
Amber英語Amber Smalltalk, Dolphin英語Dolphin Smalltalk, GemStone/S英語Gemstone (database), GNU Smalltalk, Pharo, Smalltalk/X, Squeak, VisualAge英語VisualAge, VisualWorks英語VisualWorks
衍生副語言
Self, GNU Smalltalk
啟發語言
Lisp,[4] Simula,[4] Euler英語Euler (programming language),[4] IMP英語IMP (programming language),[4] Planner英語Planner (programming language),[4] Logo[5],Sketchpad,[4] ARPAnet,[4] Burroughs B5000英語Burroughs large systems[4]
影響語言
AppleScript, CLOS, Dart, Dylan, Erlang, Etoys英語Etoys (programming language), Falcon, Go, Groovy, Io, Ioke, Java, Lasso英語Lasso (programming language), Logtalk英語Logtalk, Newspeak英語Newspeak (programming language), NewtonScript, Object REXX英語Object REXX, Objective-C, PHP 5, Perl 6, Python, Ruby, Scala, Scratch, Self

Smalltalk是一種動態類型反射式面向對象編程語言。Smalltalk由艾倫·凱、Dan Ingalls、Ted Kaehler、Adele Goldberg等於1970年代在施樂帕羅奧多研究中心開始開發。

Smalltalk對其它眾多的程序設計語言的產生起到了極大的推動作用,特別是Objective-CCLOSPythonRuby等。1990年代湧現的許多軟件開發思想都得益於Smalltalk,例如設計模式敏捷編程代碼重構[6]等。

概述

[編輯]

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

  • 一種面向對象的程序設計語言:它是一種面向對象的語言,包含語言的語法和語義。一些編譯器可以透過Smalltalk源程序產生可執行文件。這些編譯器通常產生一種能在虛擬機上運行的二進制代碼。Smalltalk語言本身非常精煉。
  • 一種程序設計環境:這裡指的是一種提供許多物件的系統,而不是某種特殊的開發環境。和許多語言不同(包括C++),Smalltalk附帶有一個巨大的、相當標準的類庫。這些使得開發Smalltalk程序的效率非常高。在其它語言(例如AdaCPascal)中,通常被作為語言的一部分的功能(例如條件判斷,循環等),在Smalltalk由特定的類提供。
  • 一個應用開發環境(ADE):由於Smalltalk的歷史原因,它具有一個非常優秀的高度集成、開放的應用開發環境。由於開發環境中的瀏覽器、監視器以及調試器,都由同樣的源程序衍生出來的,不同的版本之間也具有相當好的兼容性。此外,這些工具的源程序都可以在ADE直接存取。

歷史

[編輯]
Smalltalk-76
VisualWorks英語VisualWorks,派生於Smalltalk-80 v2的商業實現
Pharo,從Squeak v3.9分叉出的派生於Smalltalk-80 v1的開源實現

最早的Smalltalk原型由艾倫·凱於1970年代初提出。(來自Simula 67)、海龜繪圖(來自LOGO)以及圖形用戶界面(來自Sketchpad等先驅系統)等概念的有機組合,構成了Smalltalk的最初的藍圖[5]

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

  • 語言完全基於消息交換Simula 67的類的概念。
  • 語言沒有固定的語法,語法分析由類本身完成。

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

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

  • 引入了繼承和子類的概念[8]
  • 確定了語言的語法,這使得編譯器能夠產生高效、可執行、精煉的二進制代碼。
  • 拉里·泰斯勒設計了類瀏覽器,這極大地提高了Smalltalk程序員的編程效率。

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

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

  • 採取ASCII碼字符集,摒棄了原先在Smalltalk-72和Smalltalk-76中使用的特殊字符。
  • 取消了原始方法直接存取內存的能力。取而代之的是引入一系列的原始方法提供相應的功能。
  • 引入了元類的概念[9]
  • 引入MVC(模型-視圖-控制器)系統以方便交互式應用軟件的開發。

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

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

兩個當前流行的Smalltalk實現變體,是這些最初Smalltalk-80映像的後代。Squeak開源實現,它經由Apple Smalltalk[12],派生自Smalltalk-80版本1.03[13]VisualWorks英語VisualWorks經由Smalltalk-80 2.5和ObjectWorks(二者都是ParcPlace Systems的產品),派生自Smalltalk-80版本2[10]

面向對象編程

[編輯]
Smalltalk-80例子代碼在Squeak下的類層級和與之並行的元類層級的示意圖。其中的rProtoObjectcClassmcMetaclass。藍色連接表示實例聯繫,綠色連接表示繼承聯繫。

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

Smalltalk對象確切的可以做三件事:

  1. 持有狀態(引用到其他對象)。
  2. 接收消息自本身或其他對象。
  3. 在處理一個消息的過程中,發送消息至本身或其他對象。

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

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

Smalltalk是「純」面向對象編程語言,這意味着,不像C++Java,在作為對象的值和作為原始類型的值之間沒有區別。在Smalltalk中,原始值比如整數、布爾值和字符,也是對象,這麼說的意義在於它們也是相應類的實例,而且要發送消息來調用在它們上的運算。編程者可以通過子類,改變或擴展實現原始值的類,使得可以向它們的實例定義新行為,例如實現一個新的控制結構,甚至使得它們現有行為得以改變。這個事實被總結成常聽到的一句短語:「在Smalltalk中,所有東西都是對象」,它可以更精確的表達為:「所有的值都是對象」,因為變量不是。

因為所有的值都是對象,也是對象。每個類都是這個類的元類的一個實例。元類都是Metaclass(元類類)的實例,它也是對象,並且是Metaclass class(元類元類)的實例。代碼塊是Smalltalk表達匿名函數的方式,它也是對象[15]

Hello, World!例子

[編輯]

Hello, World!程序,實質上被所有計算機語言的課本用作要學習的第一個程序,它展示了這個語言的最基本語法和環境。對於Smalltalk,這個程序可極其簡單的書寫。下列代碼中,消息show:被發送給對象Transcript,具有字符串文字'Hello, World!'作為它的實際參數。調用show:方法,導致它的實際參數,即字符串文字'Hello, World!',顯示在叫做「副本」(Transcript)的終端窗口:

Transcript show: 'Hello, World!'.

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

語法

[編輯]

Smalltalk-80語法是相當極簡主義的,只基於了一小把的聲明和保留字。事實上,Smalltalk中只保留了六個「關鍵字」:truefalsenilselfsuperthisContext。它們的準確術語是「偽變量」,是服從變量標識符規則的標識符,但指示了編程者所不能變更的綁定。truefalsenil偽變量是單例實例。selfsuper,在響應一個消息而激活的方法中,指稱這個消息的接收者;但是發送給super的消息,在這個方法的定義類的超類中查找方法,而非這個接收者的類中,這允許子類中的方法調用在超類中的同名方法。thisContext指稱當前的活動記錄。

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

諺語「Smalltalk語法適合一張明信片」,所提及的是Ralph Johnson英語Ralph Johnson (computer scientist)的一個代碼片段,展示了一個方法的所有基本標準語法元素[16]

exampleWithNumber: x
  | y |
  true & false not & (nil isNil) ifFalse: [self halt].
  y := self size + super size.
  #($a #a 'a' 1 1.0)
    do: [ :each |
      Transcript 
        show: (each class name);
        show: (each printString);
        show: ' ' ].
  ^x < y

文字

[編輯]

下列例子詮釋了最常用的對象,可以在Smalltalk-80方法中被寫為文字英語Literal (computer programming)值。

數和字符

[編輯]

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

42
-42
123.45
1.2345e2
2r10010010
16rA000

最後兩個項目分別是二進制和十六進制數。在r前的數是底數或基數。基數不必須是二的冪;例如36rSMALLTALK是一個有效的數值,等價於十進制的80738163270632

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

$A

字符串

[編輯]

字符串是包圍在單引號內的字符序列:

'Hello, world!'

要在一個字符串中包括一個引號,使用另一個引號來轉義

'I said, ''Hello, world!'' to them.'

雙引號不需要轉義,因為單引號界定字符串:

'I said, "Hello, world!" to them.'

兩個相等的字符串(字符串相等,如果它們包含完全相同的字符)可以是駐留在內存不同位置中的不同對象。

符號

[編輯]

除了字符串,Smalltalk有一類叫做符號英語Symbol (programming)Symbol)的字符序列對象。符號保證是唯一的,沒有作為不同對象的兩個相等的符號。因此,符號非常易於比較,並經常用於語言構造中,比如用作消息選擇子。

符號被寫為#跟隨着字符串文字英語string literal。比如:

#'foo'

如果一個序列不包含空白或標點字符,還可以寫為:

#foo

數組

[編輯]

例如定義了四個整數的一個數組:

#(1 2 3 4)

很多實現支持下列字節數組(ByteArray)的文字語法,例如定義了四個整數的字節數組:

#[1 2 3 4]

其他

[編輯]

最後卻重要的是塊(匿名函數文字):

[... 一些smalltalk代码 ...]

很多Smalltalk方言為其他對象實現了額外的語法,但是上述的是所有方言都本質上支持的。

變量聲明

[編輯]

在各種Smalltalk中共同使用的有兩種變量:實例變量和臨時變量。其他變量和有關術語依賴於特定實現,例如VisualWorks英語VisualWorks有類共享變量和名字空間共享變量,而Squeak和很多其他實現,有類變量、池變量和全局變量。

在Smalltalk中臨時變量聲明是在方法(見後)內聲明的變量。它們聲明在方法的頂部,作為由豎槓包圍的空格分隔的名字。例如:

| index |

聲明一個臨時變量名叫index,可以包含初始值nil

多個變量可以在一組豎槓內聲明:

| index vowels |

聲明了兩個變量:indexvowels。所有變量都要初始化。字符串的索引變量,初始化為null字符或初始為0ByteArray,此外的所有變量初始化為nil

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

賦值

[編輯]

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

vowels := 'aeiou'

指定字符串'aeiou'至前面聲明的vowels變量。這個字符串是個對象(在單引號之間的字符序列是文字字符串的語法),在編譯時間由編譯器創建。

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

消息

[編輯]

消息是Smalltalk中最基礎的語言構造。所有控制結構都實現為消息發送。Smalltalk缺省的採用動態分派單一分派策略,這是相對於其他一些面向對象語言使用的多分派而言的。

一元消息

[編輯]

下列例子是發送消息factorial至數值42

42 factorial

在這種情況下,42叫做這個消息的「接收者」,而factorial是消息的選擇子。接收者通過返回一個值來相應這個消息(這個情形中是42的階乘)。同其他事物一樣,消息的結果可以賦值給一個變量:

aRatherBigNumber := 42 factorial

上面的factorial是「一元」消息,因為只涉及了一個對象,即接收者。

關鍵字消息

[編輯]

消息可以承載額外的對象作為實際參數,比如:

2 raisedTo: 4

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

'hello world' indexOf: $o startingAt: 6

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

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

new Rectangle(100, 200);

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

Rectangle width: 100 height: 200

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

二元消息

[編輯]

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

3 + 4

它發送消息+給接收者3,具有4作為實際參數傳遞(回答將是7)。類似的:

3 > 4

將消息>發送給3具有實際參數4(回答將是false)。

注意,Smalltalk-80語言自身,不包含着這些算符的含義。上述的結果,都只是這些消息的接收者(這裡是數值實例),為了響應消息+>而定義並返回的。這個機制的副作用是運算符重載,消息>可以被其他對象所理解,允許使用形如a > b的表達式來比較它們。

表達式

[編輯]

一元消息可以一個接一個的寫成方法鏈式調用

3 factorial factorial log

它發送factorial3,接着發送factorial到前面的結果6,接着發送log到前面的結果720,產生最終的結果2.85733

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

3 factorial + 4 factorial between: 10 and: 100

被求值如下:

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

最後的消息發送的回答,是整個表達式的結果。

組合

[編輯]

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

(3 factorial + 4) factorial between: 10 and: 100

將改變表達式含義,首先計算3 factorial + 4產生10。接着10接收第二個factorial消息,產生36288003628800接着接收between:and:,回答false

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

3 + 4 * 5

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

3 + (4 * 5)

複合

[編輯]

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

下列(假想)例子中,書寫了一序列的表達式,每個都用點號分隔。這個例子首先建立類Window的一個新實例,存儲它在一個變量中,接着向它發送兩個消息:

| window |
window := Window new.
window label: 'Hello'.
window open

級聯

[編輯]

如果像上述例子這樣,將一序列消息都發送給相同的接收者,它們也可以寫為方法級聯調用,具有用分號分隔的單獨消息:

Window new
  label: 'Hello';
  open

這種將前面例子的重新為一個單一表達式,避免了對將新窗口存儲在臨時變量的需要。依據平常的優先級規則,首先向Window類發送一元消息new,接着向new回答的那個對象,發送label:open

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

代碼塊

[編輯]

頭等對象。代碼塊即匿名函數,可以被表達一個文字值(它是一個對象,因為所有值都是對象)。這是通過方括號達成的:

[ :params | <消息表达式> ]

這裡的:params是代碼可以接受的形式參數的列表。結果的塊對象可以形成一個閉包:它可以在任何時間訪問它外圍的詞法作用域內的變量。這意味着下列Smalltalk代碼:

[:x | x + 1]

可以理解為:,或用λ演算表達為: :

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

[:x | x + 1] value: 3

可以被求值為:,或用λ演算表達為:

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

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

expr ifTrue: [ expr为真时求值的语句 ] ifFalse: [ expr为假时求值的语句 ]

再舉一例,向一個搜集發送消息select:

positiveAmounts := allAmounts select: [:anAmount | anAmount isPositive]

注意這與函數式編程有關,這裡的計算模式被抽象成了高階函數select:等價於在一個適當的函子上的高階函數filter[17]

控制結構

[編輯]

在Smalltalk中控制結構沒有特殊的語法。它們轉而實現為發送到對象上的消息。以條件執行為例,布爾類Boolean定義了ifTrue:ifFalse:ifTrue:ifFalse:ifFalse:ifTrue:方法。比如向一個布爾對象,發送ifTrue:消息,並傳遞一個代碼塊作為實際參數,這個塊被執行當且僅當布爾接收者為真。下面用一個例子來展示:

result := a > b
  ifTrue: [ 'greater' ]
  ifFalse: [ 'less or equal' ]

塊也被用來實現,用戶定義控制結構、枚舉器訪問者異常處理、可插拔的行為和很多其他模式。

迭代

[編輯]

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

| aString vowels |
aString := 'This is a string'.
vowels := aString select: [:aCharacter | aCharacter isVowel].

在最後一行,向字符串對象aString發送一個select:消息,它具有一個代碼塊[:aCharacter | aCharacter isVowel]作為實際參數。這個代碼塊,表示一個測試,代碼塊文字將被用作一個謂詞函數,它回答true,當且僅當這個字符串的一個元素aCharacter,應當被包括在滿足這個測試的字符搜集之中。

字符串類String響應select:消息,要調用的select:方法,定義並實現在搜集類Collection[18];它將給select:的實際參數選擇塊,傳送給形式參數aBlock;然後將綁定了選擇塊的aBlock嵌入到迭代塊的代碼之中,再把這個迭代塊作為向字符串自身發送的do:消息的實際參數,從而將這個字符串所包含的每個字符,都作為實際參數傳送給這個迭代塊,而各做一次求值。在求值迭代塊的時候,通過value:消息,將迭代元素傳送給aBlock所綁定的選擇塊,它回答一個布爾值;接着向它發送ifTrue:消息,如果這個布爾值是對象true,則將這個字符增加到要返回的字符串中。

字符串類String響應do:消息,要調用的do:方法,定義在可迭代類Iterable[19],而實現在可序列化搜集類SequenceableCollection[20],這個類是Iterable類的子類和String類的超類。

異常處理

[編輯]

Smalltalk的異常處理機制,Exception類及其子類比如Error類,類似於CLOS的異常處理樣式,使用塊作為處理器:

[ 一些运算. 
  Error signal: 'an error occurred'.
  另一些运算
] on: Error do: [ :ex | 
  处理器代码. 
  ex return ]

異常處理器的ex實際參數,提供對掛起運算的狀態的訪問,比如它的棧幀、行號、接收者和實際參數等,並且通過發送ex proceedex rejectex restartex return之一,還可用來控制計算怎樣繼續。

[編輯]

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

定義

[編輯]

下面是個平凡的類定義[21]

Object subclass: #MessagePublisher
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Smalltalk Examples'

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

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

在Smalltalk中,實例變量英語Instance variable是這個實例的私有變量,它可以在定義它們的類的任何實例方法中,還有在它的子類中定義的方法中,通過名字來訪問。在需要於一個類的所有實例、這個類本身和它的子類之間,共享某個數據的時候,要使用類變量英語Class variable,它是由這個類和它的所有實例共享的私有變量。池變量是在可以沒有繼承關聯的多個類之間共享的變量。池變量最初存儲在池字典中,現在它們應當被定義為專門的類(SharedPool的子類)的類變量。

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

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

類不能直接訪問它的實例的實例變量,而實例不能訪問它們的類的類實例變量。如果需要的話必須定義變異子與訪問子

方法

[編輯]

當一個對象接收到一個消息的時候,調用匹配這個消息名字的一個方法。所有方法都是公開的和虛擬的(也就是動態查找的)。方法被分組入指示它們意圖的協議(protocol)之中。

為一個類增加方法涉及到Behavior類,其中的compile:方法,編譯一個方法源代碼並返回一個CompiledMethod類的實例;addSelector:withMethod:方法,將給定的一個編譯過的方法增加到方法字典中。在Behavior類的子類ClassDescription類中,compile:classified:方法,編譯一個方法的源代碼,並為這個方法指派上給定的歸類(對於方法稱為協議)。

對象負責在運行時間動態的確定,執行哪個方法來響應一個消息,儘管在很多語言中,這可能是(有時或總是)在編譯時間靜態確定的。下列代碼定義一個方法publish,並且這個定義將在這個對象收到publish消息的時候發生。

publish
  Transcript show: 'Hello World!'

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

quadMultiply: i1 and: i2
  "这个方法将给定的两个数相乘并对结果乘以4."
  | mul |
  mul := i1 * i2.
  ^mul * 4

執行任何前導了^脫字符)的表達式,都導致這個方法於這一點退出,並返回這個表達式的值。終止而沒有顯式返回某個表達式的一個方法,將隱含的返回自身。

實例化

[編輯]

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

MessagePublisher new

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

publisher := MessagePublisher new

但是也可以向一個臨時的匿名對象發送一個消息:

MessagePublisher new publish

類方法

[編輯]

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

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

| rectangles aPoint collisions |
rectangles := OrderedCollection
  with: (Rectangle left: 0 right: 10 top: 100 bottom: 200)
  with: (Rectangle left: 10 right: 10 top: 110 bottom: 210).
aPoint := Point x: 20 y: 20.
collisions := rectangles select: [:aRect | aRect containsPoint: aPoint].

反射

[編輯]

反射是一個計算機科學術語,適用於有能力檢查它們自己的結構的軟件程序,例如檢查它們的分析樹或輸入和輸出參數的數據類型。反射是動態、交互式語言比如Smalltalk和Lisp的一個特徵。具有反射的交互式程序(要麼解釋的要麼編譯的)維護所有內存內對象的狀態,包括代碼對象自身,這是在解析/編譯期間生成的,並且是在編程上可訪問和修改的。

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

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

因為類是對象,可以向它們提問比如:「你實現了哪些方法?」或「你定義了什麼字段/槽/實例變量?」。所以通過能應用於系統中的任何對象的普通的代碼,對象可以輕易的檢查、複製、(去)序列化,諸如此類[23]

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

Smalltalk如何使用反射的一個例子,是處理錯誤的機制。當一個對象被發送了一個它沒有實現的消息的時候,虛擬機發送給這個對象doesNotUnderstand:消息,具有這個消息的實化作為實際參數。這個消息(它是另一個對象,是Message的實例),包含這個消息的選擇子和它的實際參數的一個Array。在交互式Smalltalk系統中,doesNotUnderstand:的缺省實現,是打開一個錯誤窗口(一個Notifier)向用戶報告錯誤。通過它和反射設施,用戶可以檢查錯誤在其中發生的上下文,重新定義犯錯的代碼,並繼續,這一切都在這個系統之中,使用Smalltalk-80的反射設施[25][26]

通過建立只理解(實現)doesNotUnderstand:的一個類,可以建立一個實例,經由它的doesNotUnderstand:方法能攔截發送給它的任何消息。這種實例可以叫做透明代理(proxy)[27]。可以使用這種代理來實現很多設施,比如分布式Smalltalk,這裡的消息在多個Smalltalk系統之間交換,和數據庫接口,這裡的對象透明的從數據庫中排除錯誤,還有promise等。分布式Smalltalk的設計影響了如CORBA這樣的系統。

基於映像的持久存儲

[編輯]

多數流行的編程系統,將靜態的程序代碼(以類定義、函數或過程的形式),分離於動態的或運行時間的程序狀態(比如對象或其他形式的程序數據)。它們在程序啟動的時候裝載程序代碼,而任何先前的程序狀態必須從配置文件或其他數據源顯式的重新建立。程序(和編程者)未顯式保存的設置,在每次重啟時都必須再次設立。傳統的程序在每次程序保存一個文件、退出和重載的時候,還失去很多有用的文檔信息。這會失去細節比如回退歷史或光標位置。基於映像的系統不會因為計算機關閉或OS更新,而強制失去所有這些東西。

但是很多Smalltalk系統,不區分程序數據(對象)和代碼(類)。事實上,類也是對象。因此,多數Smalltalk系統,存儲整個程序狀態(包括類和非類對象二者)在一個映像英語system image文件之中。這個映像可以接着由Smalltalk虛擬機裝載,將類Smalltalk系統恢復成先前的狀態[28]。這是受到了FLEX的啟發,它是Alan Kay創建的語言並描述於他的科學碩士畢業論文中[29]

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

實現列表

[編輯]

OpenSmaltalk

[編輯]

OpenSmaltalk VM(OS VM)是Smalltalk運行時環境的著名實現,很多現代Smalltalk VM基於或派生自它[31]。OS VM自身是從一組Smalltalk源代碼文件(它們叫做VMMaker),轉譯成原生C語言源代碼(通過使用叫做Slang的轉譯器[32][33]),它依次再針對特定平台和硬件架構來編譯,實際上確使Smalltalk映像的跨平台執行。源代碼可以在GitHub上獲得並在MIT許可證下發布。OS VM的知名派生者有:

  • Squeak,一個開源Smalltalk。
  • Pharo Smalltalk,一個開源跨平台語言。
  • Croquet VM,Croquet OS的一個與Squeak有關的Smalltalk VM。
  • Cuis-Smalltalkf[34],一個開源的小型、簡潔和適用的Smalltalk。
  • Haver-Smalltalk[35],Cuis的具有完整模塊系統的擴展。

JavaScript VM

[編輯]

其他

[編輯]

參見

[編輯]

引用

[編輯]
  1. ^ https://wiki.squeak.org/squeak/172.
  2. ^ Alto I Schematics (PDF). Bitsavers: 54. [21 July 2016]. (原始內容 (PDF)存檔於2021-02-24). 
  3. ^ History of Computers and Computing, Birth of the modern computer, Personal computer, Xerox Alto. [2016-04-19]. (原始內容存檔於2020-12-05). 
  4. ^ 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 Kay, Alan; Stefan Ram. E-Mail of 2003-07-23. Dr. Alan Kay on the Meaning of 「Object-Oriented Programming」. 2003-07-23 [2009-01-03]. (原始內容存檔於2020-09-16). 
  5. ^ 5.0 5.1 Alan Kay. The Early History of Smalltalk. 1993 [2021-03-06]. doi:10.1145/155360.155364. (原始內容存檔於2011-04-29). What Sketchpad called masters and instances, Simula called activities and processes. Moreover, Simula was a procedural language for controlling Sketchpad-like objects, thus having considerably more flexibility than constraints (though at some cost in elegance) ……. ……
    This Smalltalk language (today labeled -71) was very influenced by FLEX英語Flex (programming language), PLANNER英語Planner (programming language), LOGO, META II英語META II, and my own derivatives from them. ……
    One of the styles retained from Smalltalk-71 was the comingling of function and class ideas. In other works, Smalltalk-72 classes looked like and could be used as functions, but it was easy to produce an instance (a kind of closure) by using the object ISNEW. ……
    Overlapping windows were the first project tackled (With Diana Merry) after writing the code to read the keyboard and create a string of text. ……
    One of the next classes to be implemented on the Interim Dynabook (after the basics of numbers, strings, etc.) was an object-oriented version of the LOGO turtle implemented by Ted. ……
    Our early 「LISP-pair」 definition is an example of an abstract data type because it preserves the 「field access」 and 「field rebinding」 that is the hallmark of a data structure. …… What I got from Simula was that you could now replace bindings and assignment with goals. ……
    Where does the special efficiency of object-oriented design come from? …… Four techniques used together – persistent state, polymorphism, instantiation, and methods-as-goals for the object – account for much of the power. None of these require an 「object-oriented language」 to be employed – ALGOL 68 can almost be turned to this style – and OOPL merely focuses the designer’s mind in a particular fruitful direction. ……
    Simula-I had neither classes as objects nor inheritance. Simula-67 added the latter as a generalization to the ALGOL-60 <block> structure. ……
    On the other hand, since things can be done with a dynamic language that the difficult with a statically compiled one, I just decided to leave inhertance out as a feature in Smalltalk-72, knowing that we could simulate it back using Smalltalk’s LISPlike flexibility. ……
    By the time Smalltalk-76 cam along, Dan Ingalis had come up with a scheme that was Simula-like in its semantics but could be incrementally changed on the fly to be in accord with our goals of close interaction.
     
  6. ^ Where Did Refactoring Come From?. sourcemaking.com. [17 December 2013]. (原始內容存檔於2016-03-31). 
  7. ^ Carl Hewitt英語Carl Hewitt; Peter Bishop, Richard Steiger. A Universal Modular Actor Formalism for Artificial Intelligence (PDF). IJCAI. 1973 [2022-04-11]. (原始內容 (PDF)存檔於2021-02-25). Alan Kay whose FLEX and SMALLTALK machines have influenced our work. Alan emphasized the crucial importance of using intentional definitions of data structures and of passing messages to them. This paper explores the consequences of generalizing the message mechanism of SMALLTALK and SIMULA-67; ……. 
  8. ^ Learning Research Group. How To Use the Smalltalk-76 System (PDF) (報告). Xerox Palo Alto Research Center. October 1979 [2022-03-12]. (原始內容 (PDF)存檔於2022-04-12). To define a new class, select a class category in the first pane of the browse window. This selection specifies the category to which the new class will be added, and causes a template to appear in the largest pane of the browse window, the code pane. ……
    The template presented in the code pane looks as follows
        Class new title: ’NameofClass’
        subclassof: Object
        fields: ’names of fields’
        declare: ’names of class variables’
     
  9. ^ Alan Kay. The Early History of Smalltalk. [2021-03-06]. (原始內容存檔於2011-04-29). The most puzzling strange idea – at least to me as a new outsider – was the introduction of metaclasses (really just to make instance initialization a little easier – a very minor improvement over what Smalltalk-76 did quite reasonably already).
    Peter’s 1989 comment is typical and true: 「metaclasses have proven confusing to many users, and perhaps in the balance more confusing than valuable.」 In fact, in their PIE system, Goldstein and Bobrow had already implemented in Smalltalk on 「observer language」, somewhat following the view-oriented approach Ihad been advocating and in some ways like the 「perspectives」 proposed in KRL [Goldstein *].
    Once one can view an instance via multiple perspectives even 「sem-metaclasses」 like Class Class and Class Object are not really necessary since the object-role and instance-of-a-class-role are just different views and it is easy to deal with life-history issues includeding instantiation. This was there for the taking (along with quite a few other good ideas), but it wsn’t adopted. My guess is that Smalltalk had moved into the final phase I memntioned at the beginning of this story, in which a way of doing things finally gets canonized into an inflexible belief structure.
     
  10. ^ 10.0 10.1 Smalltalk-80 v2 image. [2022-02-16]. (原始內容存檔於2022-03-20). 
  11. ^ Draft American National Standard for Information Systems - Programming Languages - Smalltalk (PDF). squeak.org. [2022-02-09]. (原始內容 (PDF)存檔於2021-10-20). 
  12. ^ Apple Smalltalk 80. [2022-05-22]. (原始內容存檔於2022-05-30). 
  13. ^ Squeak V1 Sources. [2022-02-24]. (原始內容存檔於2017-06-27). 
  14. ^ Kay, Alan. Prototypes vs Classes (e-mail on Squeak list). October 10, 1998 [2021-03-06]. (原始內容存檔於2021-02-16). 
  15. ^ Goldberg, Adele; Robson, David. Smalltalk-80 The Language. Addison Wesley. 1989: 31, 75–89. ISBN 0-201-13688-0. 
  16. ^ Squeak: A minimalist syntax! (PDF). [2021-03-06]. (原始內容 (PDF)存檔於2018-06-13). 
    Object-Oriented Design with Smalltalk — a Pure Object Language and its Environment (PDF). [2021-03-06]. (原始內容 (PDF)存檔於2021-01-21). 
  17. ^ Goldberg, Adele; Robson, David. Smalltalk-80 The Language. Addison Wesley. 1989: 17–37. ISBN 0-201-13688-0. 
  18. ^ Collection Method Definitions:
    select: aBlock 
      "Answer a new instance of a Collection containing all the elements
       in the receiver which, when passed to aBlock, answer true"
      | newCollection |
      newCollection := self copyEmpty.
      self do:
        [:element | (aBlock value: element) ifTrue: [newCollection add: element]].
      ^newCollection
    
  19. ^ Iterable Method Definitions:
    do: aBlock
      "Enumerate each object of the receiver, passing them to aBlock"
      self subclassResponsibility
    
  20. ^ SequenceableCollection Method Definitions:
    do: aBlock
      "Evaluate aBlock for all the elements in the sequenceable collection"
      1 to: self size do: [:i | aBlock value: (self at: i)]
    

    Interval Method Definitions:

    do: aBlock
      "Evaluate the receiver for each element in aBlock"
      | i |
      i := start. step > 0 
        ifTrue: [[i <= stop] whileTrue: [aBlock value: i. i := i + step]]
        ifFalse: [[i >= stop] whileTrue: [aBlock value: i. i := i + step]]
    
  21. ^ Goldberg, Adele; Robson, David. Smalltalk-80 The Language. Addison Wesley. 1989: 39–53. ISBN 0-201-13688-0. 
  22. ^ Collection Method Definitions:
    Collection class >> with: firstObject with: secondObject 
      "Answer a collection whose only elements are the parameters
       in the order they were passed"
        ^(self new)
          add: firstObject;
          add: secondObject;
          yourself
    
  23. ^ Clark, A.N. Metaclasses and Reflection in Smalltalk. 1997. 
  24. ^ Ducasse, Stéphane; Lienhard, Adrian; Renggli, Lukas. Seaside – A Multiple Control Flow Web Application Framework (PDF). scg.unibe.ch. Software Composition Group Institut fur Informatik und angewandte Mathematik Universitaat Bern, Switzerland. [16 December 2013]. (原始內容 (PDF)存檔於2020-01-31). 
  25. ^ Foote, Brian; Johnson, Ralph. Reflective Facilities in Smalltalk-80. Oopsla '89. 1–6 October 1989: 327–335 [16 December 2013]. ISBN 0897913337. doi:10.1145/74877.74911. (原始內容存檔於2021-03-24). 
  26. ^ Smith, Brian C. Procedural Reflection in Programming Languages. MIT Technical Report. 1982-01-01, (MIT-LCS-TR-272) [16 December 2013]. (原始內容存檔於2015-12-13). 
  27. ^ Denker, Marcus; Peck, Mariano Martinez; Bouraqadi, Noury; Fabresse, Luc; Ducasse, Stéphane. Efficient Proxies in Smalltalk (PDF). [2021-03-07]. (原始內容 (PDF)存檔於2021-03-03). 
  28. ^ Image-Based Persistence. book.seaside.st. [17 December 2013]. (原始內容存檔於2013-12-17). 
  29. ^ Kay, Allen. FLEX – A flexible extendable language. University of Utah MSC Thesis. 1968 [2021-03-08]. (原始內容存檔於2018-07-18). 
  30. ^ Fowler, Martin. Memory Image. martinfowler.com. [17 December 2013]. (原始內容存檔於2021-05-07). 
  31. ^ OpenSmalltalk/opensmalltalk-vm, OpenSmalltalk, 2020-11-03 [2020-11-08], (原始內容存檔於2021-03-10) 
  32. ^ Slang. wiki.squeak.org. [2020-11-08]. (原始內容存檔於2021-06-08). 
  33. ^ A Guide to the S-Lang Language (v2.3.0): Preface. jedsoft.org. [2020-11-08]. (原始內容存檔於2019-08-27). 
  34. ^ Cuis Smalltalk. [2022-02-16]. (原始內容存檔於2022-02-07). 
  35. ^ Haver-Smalltalk. [2022-02-12]. (原始內容存檔於2022-02-12). 
  36. ^ PharoJS. [2022-05-17]. (原始內容存檔於2022-05-01). 
  37. ^ SqueakJS. [2022-05-17]. (原始內容存檔於2022-05-01). 
  38. ^ Smalltalk/X. [2022-02-12]. (原始內容存檔於2022-04-13). 
  39. ^ VA Smalltalk. [2022-02-25]. (原始內容存檔於2022-04-13). 
  40. ^ SmallJ. [2022-05-17]. (原始內容存檔於2021-03-01). 
  41. ^ SmallWorld. [2021-03-07]. (原始內容存檔於2009-02-26). 
  42. ^ TruffleSqueak. [2022-02-12]. (原始內容存檔於2022-04-16). 
  43. ^ www.graalvm.org. [2022-02-12]. (原始內容存檔於2022-03-17). 

延伸閱讀

[編輯]

外部連結

[編輯]