programming  

Dec 9, 2016 • Michael Chen

對於程式語言有興趣的讀者,可能會主動接觸數個主流市場的程式語言,包括 C++、Java、Python 等,甚至,會開始將注意力伸向其他相對冷門的語言,像是 Clojure、Haskell 等。除了學習許多不同的語言外,是否也有想過創造自己的程式語言呢?如果我們將 Python 視為軟體,那麼,Python 命令稿就是操作 Python 的方法。程式語言背後的執行者就是編譯器和直譯器,而製作編譯器和直譯器不是黑魔法,而是有科學的方法可遒循。那麼,我們要如何切入程式語言的領域呢?

一個通用型程式語言的流行,不是一朝一夕可成的。松本行弘 (Ruby 發明者) 就曾經說過,程式語言大約要經過十年的時光,才能變成廣泛討論及使用的語言。而我們要開發一個新的語言,是否一定要用 C 語言從頭開始打造呢?我們可以觀察 Perl、Python、Ruby 這三種程式語言,如果有經驗的讀者可以發現,這三種程式語言解決的問題領域有一部分是重曡的,但是卻各自用 C 語言從頭打造自己的執行環境。這三個語言花了超過二十年的時間,才累積了各自的社群資源。我們是否還需要再用 C 語言從頭打造另一個通用型直譯語言,再花十至二十年打造其生態圈呢?這個議題值得思考。

從前,Java 平台就是用來執行 Java 程式碼編譯出來的 bytecode。近年來,有數個新興語言不約而同地建立在 Java 平台之上,包括 Groovy、Clojure、Scala、Kotlin、Jython、JRuby 等,使得 Java 平台不再只是執行 Java 程式碼的平台,而是 JVM 語言的共通資産。如同 Perl、Python、Ruby 和 C 語言的關係,這些 JVM 語言也可以利用 Java 平台的函式庫,或是利用 Java 語言改寫效能瓶頸的部分,真是非常奇妙的感覺。這都要感謝 Java 平台在近年來效能提升,使得 JVM 語言有著足夠快的速度。像是 Rich Hickey (Clojure 發明者) 就在其設計理念中提到他看好 Java 這個跨系統的共通平台,故選擇 JVM 作為其執行環境。

另外一股不容小覻的新興勢力就是 D、Go 或 Rust 等新興編譯語言。即使 C 語言已經超過 40 歲,C++ 已經問世 30 年,仍然還是有新的利基點。比起 JVM 語言,這些語言的程式碼轉化成機械碼,在相同的演算法下,少了一層額外的執行環境,往往可以得到更佳的運行速度。以 Rust 為例,在一些 benchmark 的結果中,Rust 産生的機械碼已經比等效的 Java 程式效能更好,而接近等效的 C++ 程式碼所産生的機械碼 (可見這裡)。比起傳統的 C/C++,這類語言撰寫起來的感覺更接近高階語言,通常也意味著更短的開發時間。這類新興編譯語言的編譯環境通常是跨平台的,意味著我們的程式碼也是跨平台的。雖然這些新興編譯語言的社群資源不若傳統的 C、C++、Java 來得豐富,仍然值得關注其發展。

不過,上述的各種程式語言,仍然是通用型程式語言,即使我們可以直接利用 Java 平台或一些新興編譯語言,仍然要花一段不小時間才能建立新的程式語言。比起通用型程式語言,領域專用語言 (DSL,Domain Specific Language) 著眼在較特定的利基市場,往往不需要實作通用型程式語言的所有功能,而僅需著眼在表達特定的領域。像是 GNU/Linux 上的 Glade 語言和 Windows 上的 XAML 語言,就是用來建立 UI,而這類語言,通常會搭配 RAD 工具,以視覺化的方式建立 UI,開發者很少需要手動去撰寫相關 XML 檔案。像是 Unix-like 系統的 sed(1) 命令稿,甚至沒有明確的迴圈。有些 DSL,看起來很像通用型程式語言,卻有著明確的利基市場。像是 Zephir 讓 PHP 開發者能用類似 PHP 的高階語法撰寫 PHP extension,避開了相對低階的 C 語言。Cython 也扮演著類似的角色。由此可知,DSL 沒有制式的規格,而隨著需求可大可小。

根據 Martin Fowler 所提出的分類,DSL 可分為 external DSL 和 internal DSL 兩種。make(1) 就是一個 external DSL 的實例,雖然 make 是以 C 實作,但 Makefile (make 命令稿) 卻和 c 有著完全不同的語法,也不能引用 c 的函式庫,可視為一個完全獨立的語言。Sinatra 則是一個 internal DSL,一個 Sinatra 專案同時也是一個 Ruby 專案,只是加入 Sinatra 特有的語法。我們在開發 DSL 時,要選擇 external 還是 internal DSL 呢?這沒有一定的定論,要看該 DSL 的性質而定。甚至,我們其實不需要 DSL。從 library、framework、internal DSL 到 external DSL,這是不同層次的抽象化。有時候,良好的 library 或 framework 或許就可以解決我們的問題。在有足夠的利基市場下,DSL 才會顯出其優點。AWK 就是一個很好的例子,在當時,只有 Unix 命令列工具和 shell script,缺乏良好的腳本語言,AWK 的出現,適時地填補了當時的空缺。以現在 (2016年) 的時間背景下,有許多的高階直譯語言可用,AWK 就相對落沒了。

那麼,DSL 的利基市場在那裡呢?對於有資訊背景的程式設計者來說,如果能用 Python 或 Java 輕易解決的問題,卻要在另一個語言重新實作,那麼,為什麼要花力氣去重造輪子呢?然而,對於非資訊背景的程式使用者來說,如果某個語言簡單易學,又可以滿足其特定的需求,還是有一定吸引力的。這些使用者不會在乎 Java 的傳統或 Python 的走紅,即使像是 RPG Maker 這種來做 RPG 遊戲的 DSL,仍然有一定數量的使用者。恰當的語法,搭配適宜的開發環境,即使是一個相對小的利益市場,仍然是可以切入的。以 web 程式設計來說,JavaScript 的地位已經是確立的,然而,由於 JavaScript 本身設計的缺陷,許多新興語言湧入此利益市場,像是 CoffeeScript、TypeScript、Dart、Babel 等,雖然這些程式語言不能互相取代對方,但卻各自有自己的市場。

筆者所學有限,也無法預言下一個 DSL 的市場在那裡。除了傳統的資訊教育外,有時多多接觸其他的領域,或許就能發現下一個 DSL 的藍海。開始動手做自己的直譯器吧,即使是簡易的工程計算機,也是一個起點。累積了足夠的經驗後,也許就有機會做出一個有價值的 DSL。