Meta Lua |
|
Metalua 向下相容於 Lua 5.1。你可以動態地新增新的語句至語言中,並提供元程式,即在編譯器編譯另一個檔案時執行的程式,並且可以讀取 / 修改 / 寫入 / 編輯正在編譯的程式。
它的實用興趣是你可以延伸語言,可以使用其他語言的概念進行匯入:你可以定義一個新語法,以及一個處理函式,將這個新語句轉換為一個有效的(儘管可能會比較長,比較難讀和維護)Lua 程式。範例包括適當地支援各種 OO 風格、ML 風格的結構性模式比對、契約設計、靜態或執行期間的類型檢查、惰性評估、解析式列表產生...
Metalua 的開發仍處於 alpha 階段。
有效地控制任何資料的祕訣,是要在適當的抽象層級中呈現它。程式可以用許多方式呈現:ASCII 來源檔案、符號串流、抽象語法樹、位元組碼、編譯的二進位檔案...然而,共識認為最實用的自動程式控制呈現法,是抽象語法樹 (AST)。由於人類使用者偏好純文字來源,Metalua 讓你使用 AST 和純文字,可以依照你看到的方式將它們混和在一起,並提供將一種格式轉譯成另一種格式的便利設施。
撰寫 metalua 延伸程式並非小事:你需要知道 AST 結構,清楚了解元層級是什麼,並能輕鬆地在不同元層級之間切換而不會搞混。顯而易見地,精通 Lua 是必須的。
然而,重要的是要瞭解,儘管設計延伸程式通常不容易,但使用設計良好的延伸程式應該是容易的。
此設計符合 Lua 的設計:即使是非開發人員,也可以輕易地撰寫基礎程式。但是如果你想執行進階的工作,你需要精通非基礎的概念,例如協同程式、環境、元資料表、弱資料表、高階函式等。Lua 並非不計代價地避免查看其本質,而是在其本質中保持乾淨,以使進階使用者可以自信地對其進行駭客入侵。請注意,如果這麼難以撰寫(例如,繞著使用者資料製作一個適當 API)的話,如果它被適當地設計了,使用它應該會很輕鬆。
這種「厚顏無恥地比撰寫更容易使用」的方法也類似於 C++ 的範本,儘管假裝 C++ 很容易使用會有點牽強:如果你真的這麼想的話,那麼精通 metalua 會讓你感覺像在公園散步一樣。
如果你覺得 metalua 的獨特性是你延伸程式設計中最困難的部分,那可能表示你低估了實作正確延伸程式的複雜性,其中包括你在撰寫的那一個。
儘管在技術上,Metalua 可寫入包含許多小特設巨集的程式,就像使用 Lisp 所做的那樣,這並非預期的目的,也不予鼓勵。儘管巨集很方便,尤其是在完全開發中單一編碼人員的專案中,但就可讀性和互操作性而言,付出的代價往往不值得。
Lisp 不惜一切代價追求自由,讓使用者幾乎可以隨意撰寫所需的內容,不必接受太多指導。如果你必須使用並非由傑出駭客編寫的程式碼,這可能會變成一個問題。在另一個極端,有些極其無聊的語言難以使用,但培養出大量的有用、健全且維護良好的函式庫。對於 metalua,其目標是
由於 Metalua 會處理許多樹,因此像 ML 中那樣的模式比對非常方便。語言中提供了這類擴充功能,可寫入如下的字詞。請注意來源中存在 -{ extension 'foobar'
} 宣告:它會在編譯器中載入擴充功能,而透過列出這些擴充功能,你可以知道所使用的語言是哪一種方言。此外,你也不需要記住如何編譯每個單獨的原始檔:此資訊會儲存在其中。
---------------------------------------------------- -- Lambda-Calculus evaluator (weak head normal form) ---------------------------------------------------- -{ extension "match" } function replace(var, newval, term) match term with | `Var{ v } if v==var -> return newval | `Var{ _ } -> return term | `Lambda{ v, _ } if v==var -> return term | `Lambda{ v, b } -> return Lambda{ v, replace(var, newval, b) } | `Apply{ f, x } -> return `Apply{ replace(var, newval, f), replace(var, newval, x) } end end function reduce_whnf(term) match term with | `Apply{ `Lambda { param, body }, arg } -> local x = replace (param, arg, body) return reduce_whnf(x) | _ -> return term end end
有趣的是,上面的範例與 OCaml 非常接近,而模式比對的語法便取自 OCaml
(* Type declaration: OCaml is a statically typed language! *) type term = | Var of string | Apply of term * term | Lambda of string * term let rec replace var newval term = match term with | Var(v) when v==var -> newval | Var(_) -> term | Lambda(v, _) when v==var -> term | Lambda(v, b) -> Lambda(v, replace var newval b) | Apply(f, x) -> Apply(replace var newval f, replace var newval x) let rec reduce_whnf term = match term with | Apply(Lambda(param, body), arg) -> let x = replace param arg body in reduce_whnf x | _ -> term
最後一個範例會說明擴充功能實作的樣貌。我們將介紹經常會用到的三元選擇運算子,又稱為 C「?:」條件式。由於 Lua 已使用「:」作為方法呼叫,因此我們會讓它變成「bool ? foo, bar
」,而不是「bool ? foo : bar
」。一種正確(儘管低效率)的編碼方式是將它放在一個函式中:「(function() if bool then return foo else return bar end end)()
」。這的實作方式為
local function t_builder(bool, suffix) local foo, bar = unpack (suffix) return +{ (function() if -{bool} then -{foo} else -{bar} end end)() } end mlp.expr.suffix:add{ "?", mlp.expr, "," mlp.expr, builder=t_builder }
(記載中,一種更靈活且更有效率的實作是 與編譯器一起使用)。