Lua SBLP 2001 邀請論文

轉載自巴西程式語言研討會論文集 (2001) B-14–B-28。[ps]

延伸語言的演進:Lua 的歷史

作者:Roberto Ierusalimschy、Luiz Henrique de Figueiredo、Waldemar Celes

摘要。

自 1993 年問世以來,Lua 程式語言已遠遠超乎我們最樂觀的預期。在本文中,我們將描述 Lua 的發展軌跡,從它作為兩個特定專案的內部語言而誕生,到 2000 年 11 月發布的 Lua 4.0。我們將討論其部分概念的演進,以及其實作中的主要里程碑。

引言

有一個老笑話說:「駱駝是委員會設計出來的馬。」在程式語言界,這個笑話幾乎和關於程式語言是由委員會設計出來的傳說一樣流行。這個傳說得到 Algol 68、PL/I 和 Ada 等語言的支持,這些語言都是由委員會設計的,但並未滿足其贊助者的期望。

然而,除了委員會之外,對於這些語言部分失敗還有另一種理論:它們都是生來就偉大。它們每一個都遵循自上而下的設計流程,在任何程式設計師可以嘗試它,甚至在編譯器建置之前,語言就已經完全指定。

另一方面,大多數成功的語言都是培養出來的,而不是設計出來的。它們遵循自下而上的流程,從一個小語言開始,通常目標很小。隨著人們開始使用該語言,設計缺陷浮現,新的功能被添加(或最終被移除),有爭議的觀點被澄清(或最終被模糊化)。因此,語言的演進方式是程式語言中一個重要的研究主題。例如,SIGPLAN 已經贊助了兩次程式語言歷史研討會 [31,3]

在本文中,我們將報導 Lua 程式語言的歷史。自從作為兩個特定專案的內部語言誕生以來,Lua 已經遠遠超乎我們最樂觀的預期。我們認為,這種成功的關鍵原因在於我們最初的設計決策:讓語言保持簡單小巧;讓實作保持簡單、小巧、快速、可移植且免費。

Lua 是由一個委員會設計(或更確切地說,養育)的;一個相當小的委員會,只有三位成員,但它是一個委員會。回顧過去,我們認為由一個小委員會養育對這門語言來說非常有利。我們只在達成一致意見時才加入新功能;否則,就留待未來。稍後新增功能比移除功能容易得多。這個開發過程對於保持語言的簡潔至關重要,而簡潔是我們最重要的資產。Lua 的大多數其他品質(速度、小巧和可移植性)都源於其簡潔性。

自第一個版本以來,Lua 就有「真正的」使用者,也就是除了我們自己之外的使用者。他們始終透過建議、抱怨、使用報告和問題,對這門語言做出重要的貢獻。我們的這個小委員會再次發揮了重要的作用。它的結構賦予我們足夠的慣性,讓我們得以傾聽使用者的意見,而不必遵循他們的所有建議。

我們按時間順序組織了本文的其餘部分。我們從 1993 年導致 Lua 誕生之前的經驗開始,並繼續討論、決策、工作和樂趣長達八年。

開端

我們在 TeCGraf 使用自有設計語言的第一次經驗出現在資料輸入應用程式中。巴西石油公司 (PETROBRAS) 的工程師每天需要多次為模擬器準備輸入資料檔案。這個過程既無聊又容易出錯,因為模擬程式是需要嚴格格式化輸入檔案的舊式程式碼,通常是數字的空白欄位,沒有說明每個數字的意義。當然,每個數字都有特定的意義,工程師在看到特定模擬的圖表後,可以一目了然。PETROBRAS 要求 TeCGraf 為這種資料輸入建立幾個圖形前端。然後,只要按一下圖表的相關部分,就可以互動輸入數字,這比編輯數字欄位容易得多,而且更有意義。此外,它提供了新增資料驗證的機會,還可以從輸入資料中計算衍生量,減少使用者所需的資料量,並提高整個流程的可靠性。

為了簡化 TeCGraf 中這些前端的開發,我們決定以統一的方式編寫所有程式碼,因此我們設計了一種簡單的宣告式語言來描述每個資料輸入工作 [12]。以下是我們稱為 DEL(資料輸入語言)的這門語言中一個典型程式的區段

   :e      gasket            "gasket properties"
   mat     s                  # material
   m       f       0          # factor m
   y       f       0          # settlement stress
   t       i       1          # facing type

   :p
   gasket.m>30
   gasket.m<3000
   gasket.y>335.8
   gasket.y<2576.8
陳述式 :e 定義一個實體(在範例中稱為 gasket),其中包含一些具有預設值的欄位。陳述式 :p 定義 gasket 值的一些限制,因此實作了資料驗證。DEL 也有陳述式來指定資料輸入和輸出的方式。

DEL 中的實體基本上是傳統程式語言中的結構或記錄。不同之處在於其名稱也出現在圖形中繼檔案中,其中包含工程師執行資料輸入的相關圖表,如上所述。

這種簡單的語言被證明是成功的,無論是在 TeCGraf 中,因為它簡化了開發,還是對使用者而言,因為它簡化了資料輸入應用程式的客製化。使用者很快開始要求 DEL 提供更多功能,例如用於控制實體是否可輸入的布林表達式,而 DEL 也變得更為龐大。當他們開始要求條件式控制和迴圈時,很明顯我們需要一種真正的程式語言。

大約在同時間,我們開始為 PETROBRAS 執行另一個專案,稱為 PGM,這是一個可組態的岩性剖面報告產生器。顧名思義,此程式產生的報告高度可組態:使用者可以建立和定位軌跡、選擇顏色、字型和文字;每個軌跡可能都有網格,網格也有一組選項(對數/線性、垂直和水平刻度等);每個曲線都有自己的比例,在溢出的情況下必須自動變更;等等。

所有這些組態都必須由最終使用者(通常是地質學家或工程師)完成,而程式應該在小型機器上執行,例如執行 MS-DOS 的 PC。我們決定透過一種稱為 Sol 的專門描述語言來組態此應用程式:簡單物件語言的縮寫,在葡萄牙語中也表示太陽

由於報告產生器有許多不同的物件,每個物件都有許多不同的屬性,因此我們沒有在語言中修正這些物件和屬性。相反地,語言允許類型宣告。解釋器的主要任務是讀取描述,檢查給定的物件和屬性是否正確輸入類型,然後將資訊呈現給主程式。為了允許主程式和解釋器之間的這種通訊,後者被實作為 C 函式庫,連結到主程式。因此,主程式可以透過此函式庫中的 API 存取所有組態資訊。此外,程式可以為每個類型註冊一個回呼函式,以便解釋器在建立給定類型的物件時呼叫此函式。

以下區塊顯示 Sol 中典型的一段程式碼

   -- defines a type `track', with numeric attributes `x' and `y',
   -- plus an untyped attribute `z'. `y' and `z' have default values.
   type @track { x:number,y:number= 23, z=0}

   -- defines a type `line', with attributes `t' (a track),
   -- and `z', a list of numbers.
   -- `t' has as default value a track with x=8, y=23, and z=0.
   type @line { t:@track=@track{x=8},z:number*}

   -- creates an object `t1', of type `track'
   t1 = @track { y = 9, x = 10, z="hi!"}

   -- creates a line `l', with t=@track{x=9, y=10},
   -- and z=[2,3,4] (a list)
   l = @line { t= @track{x=t1.y, y=t1.x}, z=[2,3,4] }
Sol 的語法深受 BiBTeX [21] 和 UIL (使用者介面語言) 的影響,UIL 是一種用於描述 Motif 中使用者介面的語言 [24]

1993 年 3 月,我們完成了 Sol 語言的第一個實作,但我們從未交付。到了 1993 年年中,我們意識到 DEL 和 Sol 可以結合為一種更強大的語言。用於視覺化岩性剖面的程式很快就會需要支援程序式程式設計,以允許建立更精密的版面。另一方面,資料輸入程式也需要描述性功能來為其使用者介面進行程式設計。

因此,我們決定我們需要一種真正的程式語言,具備賦值、控制結構、子常式等功能。該語言還應該提供資料描述功能,例如 Sol 提供的功能。此外,由於該語言的許多潛在使用者並非專業程式設計師,因此該語言應避免使用難懂的語法(和語意)。最後,新語言的實作應具備高度的可攜性。

可攜性需求結果證明是其主要優勢之一:這兩個應用程式應完全可攜,語言也應如此。國營的巴西石油公司無法選擇特定的硬體,因為它只能在非常嚴格的公共資金支出規則下購買設備。因此,巴西石油公司擁有種類繁多的電腦,因此 TeCGraf 為巴西石油公司開發的軟體應可在他們擁有的每台機器上執行,其中包括 PC DOS、Windows(當時為 3.1)、Macintosh 和所有類型的 Unix。

在那個時候,我們本可以採用現有語言,而不是建立一種新語言。主要的競爭者是 Tcl [25],遠遠落後的是 Forth [26] 和 Perl [30]。Perl 不是一種延伸語言。1993 年,Tcl 和 Perl 僅在 Unix 平台上執行。這三者都有非常難懂的語法。而且它們都沒有提供對資料描述的良好支援。因此,我們開始著手開發一種新語言。

我們很快意識到,就我們的目的而言,該語言不需要類型宣告。相反,我們可以使用該語言本身來撰寫類型檢查常式,前提是該語言提供基本的自反功能(例如執行時期類型資訊)。像這樣的賦值

   t1 = @track {y = 9, x = 10, z="hi!"}
在 Sol 中有效的,在新語言中也將有效,但具有不同的含義:它使用給定的欄位建立一個物件(即關聯式表格),然後呼叫函數 track 來驗證物件(並最終提供預設值)。

由於新語言是 Sol(sun)的修改版本,TeCGraf 的一位朋友建議使用 Lua(葡萄牙語中的moon),於是 Lua 誕生了。

Lua 從 Sol 繼承了紀錄和清單建構的語法,但使用關聯式表格統一了它們的實作:紀錄使用字串(欄位名稱)作為索引;清單使用整數。除了這些資料描述功能之外,Lua 沒有新的概念;我們只想要一種輕量級的通用語言。因此,我們採用了一組小的控制結構,語法借自 Modula (whileifrepeat until)。從 CLU 中,我們採用了函數呼叫的多重賦值和多重回傳(比輸入輸出或參考參數更清爽的概念)。從 C++ 中,我們採用了允許在需要的地方宣告局部變數的絕妙構想。

少數(相當小的)創新之一是字串串接的語法。由於語言允許將字串強制轉換為數字,因此 + 符號會產生歧義;因此,我們為該運算建立了語法 ..(兩個點)。

一個爭議點是關於分號的使用。我們認為,對於具有 FORTRAN 背景的工程師來說,要求使用分號可能會有點令人困惑,但如果不允許使用分號,可能會讓具有 C 或 Pascal 背景的人感到困惑。最後,我們採用了可選分號(典型的委員會解決方案)。

最初,Lua 語言有七種類型:數字(實作為浮點數)、字串、(關聯式)表格、nil(一種具有唯一值且也稱為 nil 的類型)、使用者資料(一個通用 C 指標,用於表示 Lua 內部的 C 結構)、Lua 函數和 C 函數。(在經過八年的持續演進後,Lua 類型的唯一變更就是將 Lua 函數和 C 函數統一為單一的函數類型。)為了保持語言精簡,我們沒有包含布林類型。與 Lisp 一樣,nil 表示 false,任何其他值都表示 true。這是我們今天偶爾會後悔的少數經濟之一。

Lua 也從 Sol 繼承了作為函式庫實作的概念。實作遵循極端編程現在支持的一個原則:「最簡單且可能有效的方法」[1]。我們使用 lex 作為掃描器,並使用 yacc 作為剖析器。剖析器將程式轉換為位元組碼形式,然後由一個簡單的基於堆疊的直譯器執行。該語言有一個非常小的預定義函式庫,因為在 C 中新增函數很容易。

儘管這個實作很簡單,或者可能正是因為如此,Lua 超出了我們的預期。PGM 和資料輸入 (ED) 專案都成功地使用了 Lua [16](PGM 至今仍在使用中)。很快地,TeCGraf 內部的其他專案也開始使用 Lua。

第一年(1994-1996 年)

新使用者創造了新的需求。毫不意外地,Lua 最早的需求之一就是更好的效能。將 Lua 用於資料描述對典型的腳本語言提出了不尋常的挑戰。

我們一使用 Lua,就發現了它作為圖形元檔案支援語言的潛在用途。Lua 的資料描述功能允許將它用作圖形格式。與其他可程式化元檔案相比,Lua 元檔案的優勢在於它基於一種真正的程序語言。例如,VRML 格式使用 Javascript 來建模程序物件,導致異質(因此不乾淨)的程式碼 [2]。使用 Lua,將程序物件納入場景描述是自然而然的。程序程式碼片段可以與宣告陳述式結合,以建模複雜的物件,同時保持清晰度。

資料輸入程式(ED)是第一個將 Lua 用於其圖形元檔案的程式。一個圖表有數千個部分並不少見,這些部分使用一個包含數千個項目的 Lua 建構函數,在一個包含數百 KB 的檔案中進行描述。這意味著從程式語言的角度來看,Lua 必須應付巨大的程式和巨大的表達式。而且,因為 Lua 會動態預編譯這些程式(「即時編譯器」),這也意味著 Lua 編譯器必須非常快。追求效能的頭一個犧牲品是 lex。用一個手寫的掃描器取代由 lex 生成的掃描器,幾乎讓 Lua 編譯器的速度加倍。

我們還為建構函數建立了新的操作碼。類似於以下內容的清單建構函數的原始程式碼

   @[30, 40, 50]
類似於以下內容
   CREATETABLE
   PUSHNUMBER 1                 # index
   PUSHNUMBER 30                # value
   SETTABLE
   PUSHNUMBER 2                 # index
   PUSHNUMBER 40                # value
   SETTABLE
   PUSHNUMBER 3                 # index
   PUSHNUMBER 50                # value
   SETTABLE
使用新的方案,程式碼看起來像這樣
   CREATETABLE
   PUSHNUMBER 30                # value
   PUSHNUMBER 40                # value
   PUSHNUMBER 50                # value
   SETTABLE 1 3                 # set elements from index 1 to 3
對於一個長建構函數,不可能在儲存所有元素之前將它們全部推入堆疊中;因此,程式碼產生器會不時發出 SETTABLE 指令,以清除堆疊。

(從那時起,我們一直試圖改善編譯時間。如今,Lua 編譯一個包含 30000 個指派的程式,速度比 Perl 快六倍,比 Python 快八倍。)

我們在 1994 年 7 月發布了一個包含這些最佳化的 Lua 新版本,名稱為 Lua 1.1 [19]。這個版本可透過 ftp 下載。前一個版本從未公開發布,後來被命名為 Lua 1.0。大約在那個時候,我們也發表了第一篇描述 Lua 的論文 [10]。

Lua 1.1 有一個限制性使用者授權。它可以免費用於學術用途,但不能用於商業用途。(儘管有授權,它始終是開源的。)但那個授權沒有用。大多數競爭對手,例如 Perl 和 Tcl,都是免費的。此外,商業限制甚至會阻礙學術用途,因為許多學術專案計畫最終進入市場。因此,我們將該語言的下一版本 Lua 2.1 發布為免費軟體。

Lua 版本 2

Lua 2.1(於 1995 年 2 月發布)帶來了許多重要的變更。其中之一並非語言本身,而是語言開發的過程中:我們決定我們應始終嘗試改進語言,即使必須犧牲與先前版本的小小相容性。

在版本 2.1 中,我們實際上引入了與版本 1.1 的重大不相容性(但我們提供了一些工具來協助轉換)。我們從建構函式中移除 @,並統一使用大括弧來表示記錄和清單。移除 @ 是一項微不足道的變更,但它實際上改變了語言的感覺,而不仅仅是它的外觀。

更重要的是,我們簡化了建構函式的語意。在 Lua 1.1 中,表達式 @track{x=1, y=10} 具有特殊意義。在 Lua 2.1 中,表達式 track{x=1, y=10} 僅是 track({x=1, y=10}) 的語法糖,也就是說,它會建立一個新表格並將其作為唯一參數傳遞給函式 track

從一開始,我們就將 Lua 設計為一種延伸語言,也就是說,C 程式可以註冊自己的函式,以便從 Lua 透明地呼叫。透過這種方式,可以輕鬆地使用特定於領域的原語來延伸 Lua,以便最終使用者使用一種依其需求量身打造的語言。

在版本 2.1 中,我們引入了後備的概念:當 Lua 不知道如何進行時,就會呼叫使用者定義的函式。隨後,Lua 成為一種可以用兩種方式延伸的語言:透過延伸其「原語」函式集,以及透過後備延伸其語意。這就是我們現在將 Lua 稱為可延伸延伸語言的原因。

我們定義了算術、比較、字串串接、表格存取等的後備。當使用者設定後,只要這些運算的運算元不是所需類型,就會呼叫對應的後備函式。例如,每當兩個值相加,其中一個值不是數字時,就會呼叫後備,並將其傳回值用作加法的結果。

特別有興趣的,而且實際上是引入後備的主要原因,就是表格存取的後備:在陳述式 x=a[i] 中,如果 a[i] 為 nil,則會呼叫後備(如果已設定),並將其傳回值用作 a[i] 的值。這個簡單的新功能允許程式設計人員為表格存取實作不同的語意。特別是,可以實作數種繼承,最簡單的一種是透過委派進行單一繼承

   function Index (a,i)
     if i == "parent" then       - to avoid loop
       return nil
     end
     local p = a.parent
     if type(p) == "table" then
       return p[i]               - may trigger Index again
     else
       return nil
     end
   end

   setfallback("index", Index)

此程式碼會向上追蹤「父項」鏈,直到表格具有所需的欄位,或鏈結束為止。透過將「索引」後備設定為上述方式,即使 b 沒有 color 欄位,以下程式碼仍會印出 red

   a=Window{x=100, y=200, color="red"}
   b=Window{x=300, y=400, parent=a}
   print(b.color)

透過「父項」欄位進行委派並非任何神奇或「硬編碼」的事物。這是程式設計人員的選擇。她可以使用不同的名稱來表示「父項」欄位,或透過允許「父項」欄位本身成為依序嘗試的父項表格,或任何其他方式來實作更複雜的多重繼承。

a 不是一個表格時,會呼叫不同的後備機制來處理表達式 a[i]。有一個「可取得」後備機制,觸發以取得 a[i] 的值,例如 x=a[i];還有一個「可設定」後備機制,觸發以設定 a[i] 的值,例如 a[i]=x

利用這些表格後備機制有很多可能性;跨語言繼承是一個非常強大的機制:當 a 是使用者資料值(主機 C 程式中的指標)時,表格後備機制可以讓程式設計師透明地存取駐留在主機程式中的資料結構中的值。

我們決定不對任何這些可能的行為進行硬編碼,這導致了 Lua 的主要設計概念之一:元機制。我們沒有在語言中加入許多功能,而是提供方法讓使用者可以自行編寫功能,按照他們想要的方式,而且僅針對他們需要的功能。

後備元機制讓 Lua 可以支援物件導向程式設計,因為可以實作(各種)繼承(以及運算子重載)。我們甚至加入了一點語法糖來定義和使用「方法」:函式可以定義為 a:f(x,y,z),並將一個稱為 self 的隱藏參數新增到 a.f,也就是說,呼叫 a:f(10,20,30) 等同於 a.f(a,10,20,30)

1996 年 5 月,我們發布了 Lua 2.4。這個新版本的其中一項主要功能是一個外部編譯器,稱為 luac。這個程式會預先編譯 Lua 程式碼,並將位元組碼和字串表格儲存到二進位檔案中。這個檔案的格式被選為易於載入且可以在不同平台間移植。使用 luac,程式可以避免在執行時進行剖析和程式碼產生,這可能會很耗費資源,特別是對於大型、靜態程式,例如圖形元檔案。

我們關於 Lua 的第一篇論文 [10] 已經預期了外部編譯器的可能性,但我們直到 Lua 在 TeCGraf 被廣泛使用,而且大型圖形元檔案是用 Lua 編寫的(作為圖形編輯器的輸出)後才需要它。

除了載入速度較快之外,luac 也允許離線語法檢查,並保護原始碼不受使用者變更。然而,預先編譯並不表示執行速度較快,因為 Lua 塊在執行前總是會編譯成位元組碼 – luac 只是允許這些位元組碼儲存在檔案中,以便稍後執行。

luac 是以「用戶端模式」實作的,也就是說,它使用實作 Lua 的模組,僅作為一個(有禮貌的)用戶端,即使它包含了私人標頭檔案,以便存取需要儲存的內部資料結構。這種政策的一個優點是,它有助於將 Lua 核心實作結構化為明確分開的模組。特別是,現在可以輕鬆移除解析模組(詞法分析器、解析器和程式碼產生器),這些模組在 Lua 4.0 中佔核心程式碼的 40%,只留下載入預先編譯塊的小模組。這對於將 Lua 的微小實作嵌入到小型裝置(例如行動裝置或機器人)中很有用(Crazy Ivan 是一個在 2000 年和 2001 年於丹麥贏得機器人足球世界盃的機器人,它的「大腦」就是以 Lua 實作的)。

國際曝光(1996–2000)

1996 年 6 月,我們在《軟體:實務與經驗》中發表了一篇關於 Lua 的學術論文 [18]。1996 年 12 月,《Dr. Dobb's》雜誌刊登了一篇關於 Lua 的文章 [11]。這些針對不同社群的出版品,開始讓 Lua 在國際上廣為人知。

在 Dr. Dobb's 刊登文章後不久,我們收到了幾則關於 Lua 的訊息。第一則訊息如下

   From: Bret Mogilefsky <mogul@lucasarts.com>
   To: "'lua@icad.puc-rio.br'" <lua@icad.puc-rio.br>
   Subject: LUA rocks!  Question, too.
   Date: Thu, 9 Jan 1997 13:21:41 -0800

   Hi there...

   After reading the Dr. Dobbs article on Lua I was very eager to check it
   out, and so far it has exceeded my expectations in every way!  It's
   elegance and simplicity astound me.  Congratulations on developing such
   a well-thought out language.

   Some background: I am working on an adventure game for the LucasArts
   Entertainment Co., and I want to try replacing our older adventure game
   scripting language, SCUMM, with Lua.

   [...]
結果證明,Bret Mogilefsky 是 LucasArts 在 1997 年發行的主要冒險遊戲《Grim Fandango》的首席程式設計師。他在另一則訊息中告訴我們:「這個遊戲有大量的內容是用 Lua 編寫的」(由他強調)。Lua 第一次用於遊戲,吸引了全球許多遊戲開發者的目光。不久之後,Lua 開始頻繁出現在遊戲新聞群組中,例如 rec.games.programmercomp.ai.games

由於 Lua 體積小、效能佳、可移植性高且容易整合,因此在擴充遊戲方面獲得極大的歡迎。現在,許多遊戲公司都使用 Lua(例如 LucasArts、BioWare、Slingshot Game Technology 和 Loewen Entertainment),而且在遊戲產業中,了解 Lua 是一項有市場價值的技能。我們估計,有一半的 Lua 使用者在某種程度上參與了遊戲程式設計,但由於遊戲產業有許多秘密,因此很難更具體地說明。例如,Bret Mogilefsky 將 Lua 改編為《Grim Fandango》,但細節當然是專有的。

將腳本語言嵌入遊戲中有許多好處。腳本語言可用於定義精靈和物件物理、管理物件 AI 和角色控制,以及處理輸入裝置事件。例如,引擎可能不知道「損壞」、「速度」、「武器」等事項。選擇一種簡單的語言,也可以讓遊戲設計師使用可程式設計的工具。這對於遊戲開發至關重要,因為設計師可以實驗他們的創作。腳本語言也允許快速建立原型,並促進除錯工具的實作。

最近,在 2000 年,LucasArts 發布了另一款使用 Lua 的遊戲:猴島逃脫,這是猴島冒險系列的第四部。在這個遊戲中,為了向 Lua 致敬,他們將遊戲中的酒吧從SCUMM(他們先前使用的語言)改名為Lua 酒吧。

除了在電腦遊戲中廣泛使用(例如:Grim Fandango、博德之門、MDK2、猴島逃脫),Lua 已在世界各地許多不同的領域中使用。

Lua 在 PUC-Rio 外部最早使用之一是在史密松尼天體物理台。他們設計了一個廣義孔徑程式,用於模擬物理障礙物對入射光子流的影響,並使用 Lua 來建模幾何形狀和入射光子流與孔徑的交互作用 [23]。此程式是支持 AXAF 程式(先進 X 射線天體物理設施)的努力的一部分,這是 NASA 四大太空天文台中的第三個。

Performance Technologies 使用 Lua 來實作熱插拔乙太網路交換器 CPC4400 的命令列介面。透過將 Lua 公開為 CPC4400 的指令碼語言,使用者可以將事件(例如連結狀態、拓撲變更偵測和 RMON 警報)與 Lua 指令碼關聯起來。

Tollgrade Communications 在其下一代電信網路測試產品DigiTest中使用了 Lua。Lua 用於使用者介面、自動化測試指令碼和結果分析。

Lua 也用於巴西的心臟研究所(Instituto do Cora��o,聖保羅)、巴西電力公司 ELETROBRAS 的研究中心 CEPEL、柏林的魏爾斯特拉斯研究所、柏林工業大學和許多其他地方。

1998 年,Cameron Laird 和 Kathryn Soraiz 在 SunWorld 雜誌中撰寫關於指令碼語言的專欄,估計「全世界可能只有數萬名 Lua 程式設計師」[20]。他們認為「使用者基礎小」,對我們來說,這是這門語言日益普及的有力象徵。

Lua 版本 3

Lua 3.0(1997 年 7 月)以更強大的標籤方法概念取代了後備。後備本質上是全域性的:每次發生事件時都會呼叫使用者函式,而且每個事件只有一個函式。這使得難以組合具有不同繼承概念的 Lua 模組,例如。儘管可以串連後備,但串連很慢且容易出錯,而且實際上沒有人這樣做。

自 Lua 3.0 以來,程式設計師可以建立標籤,並將表格和使用者資料關聯到標籤。標籤方法基本上是根據運算子的標籤選取的後備。透過標籤和標籤方法,不同的表格(和使用者資料)可以為其運算有不同的後備。

標籤概念基本上為 Lua 提供了使用者定義的類型。換句話說,標籤只是一個表示新類型的數字。當您將表格與特定標籤關聯時,您實際上是在為該表格定義一個新類型:表格的類型(或標籤)指定了它如何實作其運算子。

當我們引入後備時,它們大多數描述了 Lua 在其他錯誤事件中的行為,例如索引非表格值或呼叫非函式值。因此,我們將後備視為異常處理機制。隨著使用者定義標籤的引入,後備(現在稱為標籤方法)主要成為描述新類型行為的機制,儘管我們仍然可以使用它們來擴充基本類型的行為。

儘管有了這個新狀態,我們仍然很長一段時間將標籤方法視為例外處理機制,而且我們沒有將標籤與類型連結在一起。直到最近我們才了解到使用者定義標籤和標籤方法作為建立使用者定義類型機制的全部意義。Lua 4.1 將允許使用者為這些新類型提供名稱,從而確認這項認知(目前,只有基本類型有名稱)。

Lua 3.0 也以 C 式前處理器的形式,帶來條件編譯的支援。如同任何語言功能,它很容易新增(儘管它讓詞法分析器變得複雜),而且程式設計人員很快地開始使用它(程式設計人員會使用任何語言功能)。一旦開始使用某項功能,對更多功能的需求就會緊隨在後。最常提出的要求之一是加入巨集擴充,但在郵件清單和我們之間的冗長討論中,並沒有提出任何明確的建議。每項建議都意味著詞法分析器和解析器有很大的變更,而且好處不明顯。因此,前處理器從 Lua 3.0 到 3.2 版(兩年)都維持不變。

最後我們決定前處理器造成的危害大於好處,讓程式碼變得更龐大,而且引發使用者無止盡的討論,因此我們在 Lua 4.0 中將它移除。我們認為 Lua 現在沒有前處理器更乾淨。多年來,我們一直努力讓 Lua 更簡單,而且已經移除我們曾經視為功能,但很少程式設計人員實際使用,而且後來被認為是錯誤功能的語言中的陰暗角落。

Lua 第 4 版

在 3.2 版之前,一次只能有一個 Lua「狀態」處於活動狀態。我們確實有一個 API 函式可以變更狀態,但使用起來有點不順手。為了簡化,當我們設計 API 時,並未在函式中包含明確的狀態參數,只有一個單一的全域狀態。回顧過去,那是一個錯誤。當 Lua 3.2 推出時,很明顯許多應用程式如果能以方便的方式執行多個 Lua 狀態,會更簡單。例如,我們必須製作一個 Lua 3.2 的特殊版本,才能包含在 CGILua 中,這是網頁瀏覽器的擴充功能,用於 Lua 中的動態頁面服務和 CGI 程式設計。稍早,LucasArts 也為 Lua 3.1 做了類似的事情。

當我們討論 Lua 3.3 的計畫時,具有明確狀態的 API 是我們的最高優先順序。然而,這引發了相容性的問題。最後,因為必須有幾項不相容性,因此我們決定重寫 API,而下一個版本就成了 Lua 4.0。現在,API 不僅包含明確的狀態,而且也更易於使用且更有效率。我們擔心轉換到新的 API 會有點痛苦,因為這是我們自 Lua 1.1 以來第一次真正變更 API。我們在郵件清單中確實收到了一些抱怨,但整體而言,變更一點也不痛苦。大多數 Lua 程式設計人員不會接觸到 API,許多人只透過自動化工具使用它,而且許多人認為好處優於轉換成本。

我們在 2000 年 11 月發布了 Lua 4.0。除了新的 API 之外,這個版本還帶來了許多其他小幅度的增強功能,其中包括 for 迴圈。

所有從事程式語言工作的人都知道,人們很容易為此主題展開「宗教戰爭」。這些戰爭的一個有趣特徵是,通常主題越平凡,討論就越熱烈。例如,人們討論分號時會比討論高階函數時興奮得多。當然,其中一個原因是,前者比後者有更多人有意見。但另一個更重要的原因是,平凡的細節對人們對語言的舒適度有很大的影響。如果一個工具沒有良好的握感,那麼創造一個奇妙、深思熟慮的工具是沒有用的——沒有人會使用它。

從 1.1 版開始,for 語句就在大多數 Lua 使用者的願望清單中。最常見的抱怨是人們忘記在 while 迴圈的結尾寫入增量,從而導致無限迴圈。

我們很快就達成了一致。但是,儘管我們都同意需要一個 for 迴圈,但我們無法就任何特定結構達成一致。我們認為類似 Pascal(或類似 Modula)的結構過於嚴格,因為它沒有考慮對表格元素或檔案行的迭代。此外,將標識符 to 變成保留字將是一個不可接受的不相容性。另一方面,C 傳統中的 for 迴圈並不適合 Lua。

隨著封閉和匿名函數在 3.1 版(98 年 7 月)中的引入,我們決定使用高階函數進行迭代。(事實上,對迭代器的需求是 Lua 中引入匿名函數的主要原因之一。)Lua 3.1 推出了兩個用於迭代的預定義函數

  foreach(table, f)
  foreachi(table, f)
foreach 函數按給定表格中所有鍵值對應用 f,沒有特定順序。foreachi 函數類似,但它將表格視為一個列表(或陣列):它只遍歷具有數字索引的元素,並確保按升序遍歷它們。儘管我們只為這兩個特定遍歷提供了函數,但創建新的迭代器非常容易。

在引入這些迭代函數兩年多後,我們意識到,儘管創建新的迭代器很容易,但幾乎沒有人這樣做。第一個原因是,大多數程式設計師對匿名函數和高階函數感到不舒服,特別是在程式語言中。但第二個原因,也是我們看來最重要的原因是,幾乎沒有人需要其他迭代器。這意味著,多年來,我們一直在努力實現一種正交性,而沒有任何真實使用者真正關心。有了這種理解,我們很快設計了一個 for 迴圈,有兩種格式,一種用於數字迴圈,另一種用於遍歷表格。

自從第一個版本以來,for 語句是語言中最成功的變更之一。首先,它真正涵蓋了大多數常見的迴圈;對於真正通用的迴圈,則有 while 迴圈。其次,由於其格式嚴謹,因此可以輕鬆建立特定的操作碼來實作迴圈,這樣一個具有空主體的數字 for 迴圈的執行速度會比等效的 while 結構快兩倍以上。

結論

目前,Lua 擁有穩定的使用者基礎。Lua 有個活躍的討論串,來自 30 多個不同國家的近 500 人參與。其網站 (www.lua.org) 每天約有 500 人次造訪,來自 50 個國家。其用途從冒險遊戲到網路伺服器、電話網路測試到乙太網路交換器。

幾個 ftp 網站提供原始的 Lua 原始碼,還有其他幾個網站散布特定平台的版本,例如 Windows 的 DLL、EPOC 的 SIS、Linux 的 RPM、RISC OS 的二進位檔等。此外,幾本雜誌在補充光碟中散布 Lua(例如 Dr. Dobb's、Linux Magazine France 和日本的 C Magazine)。

作為一種語言,Lua 的主要貢獻是提供元機制而不是功能的決策結果。作為一種產品,Lua 的成功來自於其簡潔、小巧以及實作的可移植性,這讓 Lua 能夠在許多不同的平台中使用,包括小型裝置,例如掌上型電腦、手持裝置、專用電路板和機器人。Cameron Laird 和 Kathryn Soraiz 在 1998 年預測,「無所不在的嵌入式處理(汽車、管道和廚房用具中的電腦)即將爆炸,這只會對 Lua 有利」[20]。當時,我們並沒有太注意,但他們是對的。

Lua 也透過幾篇論文和關於 Lua 的論文以及使用 Lua 作為相關技術的論文,做出了幾項學術貢獻 [4,29,14,17,13,28,15,22,6,27,9,5,7,8]

成功是有代價的。隨著語言的演進,與先前版本的相容性越來越成為創新的阻礙。儘管如此,我們不允許相容性阻礙進步;它只是語言設計煉金術中的一個要素(儘管是一個強大的要素)。

最後,要維護一門語言,遠遠不只是設計它而已。在所有面向中,都必須完全注意細節:語言設計、實作、文件、建立使用者社群並傾聽他們的意見,同時堅持原來的設計決策。

致謝

沒有許多人的幫助,Lua 永遠不會成為現在的樣子。TeCGraf 中的每個人都以不同的形式做出貢獻 – 使用這門語言、討論它、在 TeCGraf 外傳播它。特別感謝 TeCGraf 的負責人 Marcelo Gattass,他始終鼓勵我們,並給予我們完全的語言和實作自由。Lua 實際上是第一個在網際網路上公開發布的 TeCGraf 產品,甚至早於蓬勃發展時期。

沒有使用者,Lua 就只會是另一門語言。使用者及其用途是對一門語言的最終考驗。從他們那裡,我們獲得了錯誤報告、設計缺陷、看待現實的新方法。特別感謝討論清單的成員,感謝他們的建議、抱怨,以及主要忍受我們有時專制的風格。

參考文獻

[1] K. Beck。Extreme Programming Explained: Embrace Change。Addison-Wesley,2000 年。

[2] G. Bell、R. Carey 和 C. Marrin。虛擬實境建模語言規格 – 版本 2.0。 http://www.vrml.org/VRML2.0/FINAL/,1996 年 8 月。(ISO/IEC CD 14772)。

[3] T. J. Bergin 和 R. G. Gibson,編輯。History of Programming Languages,第 2 卷。ACM Press,1996 年。

[4] A. Carregal 和 R. Ierusalimschy。Tche – 一個 Lua 語言的視覺環境。在VIII Simp�sio Brasileiro de Computa��o Gr�fica,第 227-232 頁,S�o Carlos,1995 年。

[5] W. Celes。Modelagem configur�vel de subdivis�es planares hier�rquicas。博士論文,Dep. Inform�tica,PUC-Rio,巴西里約熱內盧,1995 年。

[6] R. Cerqueira、C. Cassino 和 R. Ierusalimschy。跨不同元件軟體系統的動態元件黏合。在DOA'99 – International Symposium on Distributed Objects and Applications,第 362-371 頁,蘇格蘭愛丁堡,1999 年。IEEE 電腦學會。

[7] M. T. M. de Carvalho。Uma estrat�gia para o desenvolvimento de aplica��es configur�veis em mec�nica computacional。博士論文,Dep. Engenharia Civil,PUC-Rio,巴西里約熱內盧,1995 年 6 月。

[8] R. F. de Gusm�o Cerqueira。Um Modelo de Composi��o Din�mica entre Sistemas de Componentes de Software。博士論文,Dep. Inform�tica,PUC-Rio,巴西里約熱內盧,2000 年 8 月。

[9] M. J. de Lima、N. Rodriguez 和 R. Ierusalimschy。在分散式物件系統中,遠端函式作為一級值。在IV Simp�sio Brasileiro de Linguagens de Programa��o,第 1-14 頁,累西腓,2000 年 5 月。

[10] L. H. Figueiredo、R. Ierusalimschy 和 W. Celes。擴充應用程式的語言設計與實作。在 XXI Semish 中,第 273–284 頁,Caxambu,1994 年。

[11] L. H. Figueiredo、R. Ierusalimschy 和 W. Celes。Lua:可擴充的內嵌式語言。Dr. Dobb's Journal,21(12):26–33,1996 年 12 月。

[12] L. H. Figueiredo、C. S. Souza、M. Gattass 和 L. C. G. Coelho。繪圖資料擷取介面產生。在 SIBGRAPI '92 程序(巴西電腦繪圖與影像處理研討會),第 169–175 頁,1992 年。

[13] P. R. Gomes、B. Feij�、R. Cerqueira 和 R. Ierusalimschy。虛擬原型設計中的反應性和主動性。在 I. Horvath 和 A. Taleb-Bendiab 編輯的 第 2 屆並行工程工具與方法國際研討會,第 242–253 頁,英國曼徹斯特,1998 年 4 月。

[14] T. G. Gorham 和 R. Ierusalimschy。擴充語言的反向除錯系統。在 R. Bigonha 編輯的 第 1 屆巴西程式語言研討會,第 103–114 頁,貝洛奧里藏特,1996 年 9 月。

[15] A. Hester、R. Borges 和 R. Ierusalimschy。使用 Lua 建置彈性和可擴充的 Web 應用程式。Journal of Universal Computer Science,4(9):748–762,1998 年。http://medoc.springer.de:8000/.

[16] R. Ierusalimschy、W. Celes、L. H. Figueiredo 和 R. de Souza。Lua:應用程式自訂語言。在 VII Simp�sio Brasileiro de Engenharia de Software – Caderno de Ferramentas,第 55 頁,巴西里約熱內盧,1993 年。

[17] R. Ierusalimschy、R. Cerqueira 和 N. Rodriguez。使用反向性與 CORBA 介接。在 IEEE 國際電腦語言會議 (ICCL'98),第 39–46 頁,伊利諾州芝加哥,1998 年 5 月。IEEE 電腦學會。

[18] R. Ierusalimschy、L. H. de Figueiredo 和 W. Celes。Lua–可擴充的擴充語言。Software: Practice and Experience,26(6):635–652,1996 年。

[19] R. Ierusalimschy、L. H. Figueiredo 和 W. Celes。程式語言 Lua 參考手冊。Monografias em Ci�ncia da Computa��o 3/94,巴西里約熱內盧天主教教廷大學,巴西里約熱內盧,1994 年。

[20] C. Laird 和 K. Soraiz。1998 年:腳本突破年。SunWorld,1998 年 8 月。 http://sunsite.icm.edu.pl/sunworldonline/swol-08-1998/swol-08-regex.html

[21] L. Lamport。LaTeX:文件編寫系統。艾迪生-韋斯利,1986 年。

[22] M. C. Martins、N. Rodriguez 和 R. Ierusalimschy。CORBA 伺服器的動態擴充。在 Euro-Par'99 並行處理,第 1369–1376 頁,法國圖盧茲,1999 年。Springer-Verlag。(LNCS 1685)。

[23] D. Nguyen、T. Gaetz、D. Jerius 和 I. Stern。使用廣義孔徑程式建模 AXAF 障礙物。在天文資料分析軟體和系統 VI中,第 485-487 頁,1996 年 9 月。

[24] 開放軟體基金會。OSF/Motif 程式設計師指南。Prentice-Hall, Inc.,1991 年。(ISBN 0-13-640673-4)。

[25] J. Ousterhout。Tcl:可嵌入式命令語言。在1990 年冬季 USENIX 會議記錄中。USENIX 協會,1990 年。

[26] E. D. Rather、C. H. Moore 和 D. R. Colburn。Forth 的演進。在ACM SIGPLAN 程式語言歷史會議中,1993 年 4 月。

[27] N. Rodriguez 和 R. Ierusalimschy。基於 CORBA 的應用程式的動態重新組態。在 J. Pavelka、G. Tel 和 M. Barto\vsek 編輯的SOFSEM'99:第 26 屆資訊理論與實務現況趨勢會議中,第 95-111 頁,捷克共和國米洛維,1999 年。Spinger-Verlag。(LNCS 1725)。

[28] N. Rodriguez、R. Ierusalimschy 和 R. Cerqueira。使用 CORBA 元件進行動態組態。在第 4 屆組態式分散式系統國際會議 (ICCDS'98)中,第 27-34 頁,馬里蘭州安納波利斯,1998 年 5 月。IEEE 電腦學會。

[29] N. Rodriguez、C. Ururahy、R. Ierusalimschy 和 R. Cerqueira。使用直譯語言在分散式系統上實作平行演算法。在 L. Boug�、P. Fraigniaud、A. Mignotte 和 Y. Robert 編輯的Euro-Par'96 平行處理 - 第二屆 Euro-Par 國際會議中,第 597-600 頁,第一卷,法國里昂,1996 年 8 月。Springer-Verlag。(LNCS 1123)。

[30] L. Wall 和 R. L. Schwartz。Perl 程式設計。O'Reilly & Associates, Inc.,1991 年。

[31] R. L. Wexelblat 編輯。程式語言歷史。學術出版社,1981 年。