Dylan_(编程语言)

**Dylan** 是多範式的程式語言，包括了支援函數式和物件導向程式設計（OOP），它是動態和反射式的，卻提供了設計用於支援生成高效機器碼的編程模型，包括了在動態和靜態行為上的細粒度的控制。它是在 1990 年代早期由蘋果公司領導的群組創造的。

## 概述

在 Dylan 參考手冊中有簡明而徹底的語言概述。Dylan 衍生自 Scheme 和 Common Lisp，並增加了衍生自 Common Lisp 物件系統（CLOS）的整合的物件系統。在 Dylan 中，所有的值（包括數值、字元、函式和類）都是頭等物件。Dylan 支援多重繼承、多型、多分派、關鍵字參數、物件內省、基於模式的語法擴充宏和很多其他進階特徵。程式可以表達在動態性上的細粒度的控制，允許程式占據在動態和靜態編程之間的連續區，並支援演進式開發（允許先快速原型隨後增進精製和最佳化）。

Dylan 的主要設計目標是成為適合開發商業軟體的動態語言。Dylan 嘗試解決潛在的效能問題，通過向完全靈活性的 Lisp 系統介入「本性」限制，允許編譯器清晰的理解可編譯單元比如函式庫。Dylan 從 Scheme 和其他 Lisp 衍生出了它的很多語意；某些 Dylan 實現最初建造在現存 Lisp 系統之內。但是 Dylan 有著類似 ALGOL 的語法而非類似 Lisp 的字首語法。

## 歷史

Dylan 是在 1990 年代早期由蘋果公司領導的一個群組建立的。在它開發的時候，它被意圖用於 Apple Newton 電腦，但是 Dylan 實現那時還沒有達到充分成熟，而 Newton 轉而使用了 C 和 Walter Smith 開發的 NewtonScript 二者的混合。Apple 在 1995 年終止了其 Dylan 開發努力，儘管他們製作了一個可獲得的「技術發行」版本（Apple Dylan TR1），並包括了一個進階整合式開發環境（IDE）。

其他兩個小組對語言設計和開發實現做出了貢獻：Harlequin 公司發行了 Microsoft Windows 下的商業 IDE，卡內基・梅隆大學發行了叫作 Gwydion Dylan 的 Unix 下的編譯器。二者的實現分別於 2004 年和 1998 年開放了原始碼。Harlequin 實現當前叫作 Open Dylan 並由一組志願者維護。

Dylan 語言的代號是 Ralph。James Joaquin 選擇名字 Dylan 表示「動態語言」（**Dy**namic **lan**guage）。

## 語法

Dylan 的多數語法特徵來自它的 Lisp 傳承。Dylan 最初使用類似 Lisp 的字首語法，它基於了 S - 表達式。到了語言設計完成的時候，語法被變更為類似 ALGOL 的語法，預期廣泛的編程者受眾會更加熟悉它。語法由 Michael Kahl 設計。它在 Dylan 參考手冊中有詳盡描述。

### 詞法

Dylan 不是大小寫敏感的。Dylan 的詞法允許使用連字暨減號的命名約定，來連接多單詞識別碼的各部份（有時叫做「lisp-case」或「kebab case」）。這個約定在 Lisp 語言中是常見的，但不適用於將不是數值文字一部份的連字號暨減號，當作一個單一詞法記號處理的那些程式語言，即使在它沒有包圍著空白字元的時候。

除了字母數字字元和連字暨減號之外，Dylan 允許特定非字母數字字元作為識別碼的一部份。識別碼不可以單獨的由非字母數字字元或數字字元組成。如果有任何歧義，應使用空白。

### 樣例代碼

有幾個槽的一個簡單的類：

在約定上，類使用尖括號（即小於號和大於號）來命名，比如這個代碼例子中的類名字 `<point>`。

在 `end class <point>` 中，`class` 和 `<point>` 二者都是可選的。對所有 `end` 子句都是如此。例如，可以寫 `end if` 或唯寫 `end` 來終止一個 `if` 語句。

同樣的類，可以用極小化方式重寫為：

槽現在都確定類型為 `<object>`。槽必須被手動初始化。

在約定上，常數名字開始於 `$`：

階乘函式：

這裡的 `n!` 和 `<integer>` 就是正常的識別碼。

這裡沒有顯式的返回語句。一個方法或函式的結果是最後求值的那個表達式。除掉在返回位置上的表達式後面的分號是常見的風格。

## 模組與命名空間

在很多物件導向語言中，類是封裝和模組化的主要方式；每個類別定義一個命名空間並控制哪些定義是在外部可見的。進一步的，在很多語言中類別定義必須被用作一個整體的不可見單元。例如，使用 `String` 串接函式要求匯入並編譯全部的 `String`。

某些語言套件括 Dylan，還包括一個分立的顯式的命名空間或模組系統，可以用更一般性的方式進行封裝。

在 Dylan 中，編譯單元和匯入單元的概念是分開的，類對於二者都沒有什麼特殊可以言。「庫」定義應當被一起編譯和處理的專案，而「模組」定義一個命名空間。類可以一起放置在模組中，或分拆至其中，隨編程者意願。經常是一個類的完全定義不存在於一個單一的模組中，而是延展於進行選擇性收集的多個模組至上。不同的程式可以有相同的類的不同的定義，並只包括它們所需要的。

例如，考慮支援 `String` 的一個附加庫 regex。在某些語言中，一個功能要被包括在字串中，這個功能就必須被增加到 `String` 命名空間。隨著這種事情不斷發生，`String` 類變得越來越大，而不需要使用 regex 的函式仍必須為增加了庫大小付出代價。為此，這種附加件典型的放置在它們自己的命名空間和物件之中。這種方式的缺點是新函式不再是 `String` 的一部份；它轉而被隔離在單獨聲明的它自己的函式集合之中。不再使用 `myString.parseWith(myPattern)`，從 OO 視角這是自然的組織方式，而是使用像 `myPattern.parseString(myString)` 這樣的東西，它在效果上反轉了次序。

在 Dylan 之下，可以為相同代碼定義很多介面，例如 `String` 串接方法可以給放置在 `String` 介面和 `concat` 介面二者之中，後者將不同的類中的不同的串接函式收集在一起。這常用於數學庫中，這裡的函式意圖適用於廣泛的不同物件類型。

介面構造的更實際用法是建造一個模組的公開和私有版本，在其他語言中這被包括為一個附帶特徵，並總是導致問題並增加語法。在 Dylan 之下，所有的函式呼叫可以簡單的放置在「私有」或「開發」介面中，而把可公開存取的函式收集在 `Public` 介面之中。在 Java 或 C++ 之下，一個物件的可見性是定義在代碼中的，意味著要提供類似的變更，編程者將被強制的去完全重寫定義，並且不能同時有兩個版本。

## 類

在 Dylan 中以類似於大多數 OO 語言的風格，類描述了物件的 `slot`（槽，資料成員，欄位，ivar 等）。 所有對槽的存取都要通過方法，就像 Smalltalk 那樣。預設的 `getter` 和 `setter` 方法基於槽名字而自動生成。對比於多數其他 OO 語言，可應用於類的其他方法經常定義於這個類的外部，因此在 Dylan 中類別定義典型的只包括儲存的定義。例如：

在這個例子中，定義了類 `<window>`。`<类名字>` 語法只是約定，使得類名字顯得突出，尖括號只是這個類名字的一部份。與之相對比，在一些語言中，約定為大寫類名字的首字母，或給名字字首上 C 或 T（舉個例子）。`<window>` 繼承了一個單一類 `<view>`，並包含二個槽：`title` 持有這個窗口標題的字串，和 `position` 持有這個窗口一角的 X-Y 點。在這個例子中，標題給出為預設值，而位置還沒有值。可選的 `init-keyword` 語法允許編程者在初始化這個類的物件時指定這個槽的初始值。

在語言比如 C++ 或 Java 中，類還定義了它的介面。所以在這二種語言中，如果像上述案例中，類別定義沒有顯式的對可見性的指令，則對資料成員和方法的存取都被當作是 `protected`，意味著它們只能被子類使用。要使得無關代碼使用這個窗口的實例，它們必須給聲明為 `public`。

在 Dylan 中，這些槽的可見性規則不被當作這個代碼的一部份，而是模組 / 介面系統的一部份。這增加了相當大的靈活性。例如，在早期開發中用的介面可以聲明所有東西為 `public`，而用在測試和部署中用的介面會加以限制。對於 C++ 或 Java，這種變更會需要改變原始碼，所有人們就不做了，而在 Dylan 中，這是完全無關的概念。

儘管這個例子沒有用到，Dylan 還支援多重繼承。

## 方法和泛化函式

在 Dylan 中，方法不是原生的關聯於任何特定的類；方法可以被認為存在於這個類之外。就像 CLOS，Dylan 是基於多分派（多方法）的，這裡特定方法的呼叫是基於它的所有實際參數的類型來選擇的。方法不需要在編譯時間就知道，基於使用者的偏好，所要求的函式可以是能獲得到的也可以不能。

在 Java 之下，相同的方法被隔離在特定的類之中。要使用這個功能，編程者被強制去 `import` 這個類並顯式的參照它來呼叫這個方法。如果這個類不可獲得，或在編譯時間未知，這個應用簡單的不能編譯。

在 Dylan 中，泛化函式表示零或多個類似的方法，用 `define method` 方式建立的所有方法自動的包含在同名的泛化函式之內。泛化函式也可以顯式的用 `define generic` 聲明，允許編程者精確控制可以增加哪種方法。代碼被隔離於儲存而位於泛化函式中。很多類在其中擁有它們自己的方法，因此在感官上就像多數其他 OO 語言一樣。但是代碼實際上位於泛化函式之中，意味著它們不附屬於特定的類，並可以被任何人自然的呼叫。下例將類 `<window>` 的方法 `turn-blue` 併入同名的泛化函式之內：

這個定義類似於其他語言的定義，並有可能被封裝到 `<window>` 類之中。注意 `:=` 這個 `setter` 呼叫，它是 `color-setter($blue, w)` 的語法糖。

泛化函式的自主利用可見於更泛化的例子之中。例如，在多數語言中的一個常見函式 `to-string`，它返回這個物件的某種人類可讀形式。比如說一個窗口可能返回它的標題和它在父窗口中的位置，而一個字串將返回它自身。在 Dylan 中，這些方法可以給收集到叫作 `to-string` 的一個單一模組中，因而這個代碼被從這個類自身的定義中移除。如果一個特定物件不支援 `to-string`，可以簡單的在 `to-string` 模組中增加上它。

## 擴充性

上述的整體概念可能讓讀者覺得很奇怪。處理一個窗口的 `to-string` 不定義在 `<window>` 之中。除非考慮到 Dylan 對 `to-string` 呼叫的處理方式，否則就可能變得沒有意義。在多數語言中，在程式編譯的時候，尋找給 `<window>` 的 `to-string` 並把它替代為到這個方法的一個指標（或多或少）。在 Dylan 中，這發生在程式初次執行的時候，執行時系統建造一個方法名字 / 形式參數細節的表格，並通過這個表格來動態的尋找方法。這意味著一個特定方法可以位於任何地方，不只是在編譯時間單元中。最後編程者得到了在放置其代碼上的相當大的靈活性，只要合適，可以收集在它的類之中，也可以與相同功能的方法收集在泛化函式中。

這裡隱含著編程者可以通過在單獨檔案中定義函式，來向現存的類增加功能。例如人們可能希望向所有 `<string>` 增加拼寫檢查，這在多數語言中將需要存取到字串類的原始碼，而這種基本類很少以原始碼形式給出。在 Dylan（和其他可延伸語言）中，拼寫檢查方法可以增加到 `spell-check` 模組中，通過 `define method` 構造定義它可以適用的所有的類。在這種情況下，實際功能可以定義在一個單一的泛化函式中，它接受一個字串並返回錯誤。當 `spell-check` 模組被編譯入程式的時候，所有字串（和其他物件）都會得到這個增加的功能。

## 參照

## 外部連結

* 官方網站, Open Dylan – hosts open source, optimizing Dylan compiler targeting Unix/Linux, macOS, Microsoft Windows
* Overview of the language （頁面存檔備份，存於網際網路檔案館）
* An Introduction to Dylan （頁面存檔備份，存於網際網路檔案館）
* Apple Dylan TR1
* The Marlais Dylan Interpreter （頁面存檔備份，存於網際網路檔案館） – An implementation of a subset of Dylan, suitable for bootstrapping a compiler
* 開放目錄專案中的「Dylan」