2016年3月22日 星期二

好書 : 松本行弘談程式世界的未來

偶然機會在網路上看到這本書的電子版, 搜尋市圖網站發現有收藏此書就借來看看. 其實我只是想知道, 身為 Ruby 創造者的松本行弘如何看待同樣是 Dynamic Scripting 語言的 Lua. 這兩個語言有一個共通點 : Lua 是唯一起源於南美洲的程式語言; 而 Ruby 則是唯一起源於亞洲的語言, 我很好奇一個語言的設計者會怎麼看另外一個語言.
Source : 金石堂

松本行弘在本書的 3-5 節談到 Lua, 重點摘要如下 :
  1. Lua 的特徵是考慮到嵌入應用程式的通用指令稿語言, 與其想法類似的還有 Tcl, 不過 Lua 比 Tcl 更接近於通用語言. Lua 與 Tcl 為此目的都將語言規格都限制在非常小的程度, 例如 Tcl 只有字串這一個資料型別, 而 Lua 則使用一種表格結構來實作其所支持的資料型態. 
  2. Lua 在實作上完全以 ANSI C 撰寫, 實現了極高的可移植性, Lua 的虛擬機器以高速聞名. 
  3. Lua 的數值資料型別全部用浮點數儲存, 沒有整數 (Perl 也是如此). 而使用者資料型別 (User Data) 用在以 C 語言等擴充 Lua 功能時, 例如檔案 I/O 就是利用 User Data 達成.
  4. Lua 的函式是頭等物件 (first class), 即與數值等其他資料型別一樣, 可以存入物件, 當成引數傳遞, 或作為傳回值等. 
  5. Lua 最具特徵的資料型別是表格, 可實現其他語言中的 array(陣列), hash(關聯式陣列), object(物件) 等結構, 是萬能資料型別. PHP 也是 array 與 hash 合而為一, 而 Javascript 雖然也有關聯式陣列, 但與陣列是獨立的資料結構.
    array={1,2,3}     --陣列
    print(array[1])    --輸出 1
    hash={x=1,y=2,z=3}   --關聯式陣列
    array["x"]=1    --array 與 hash 混合 (賦值方法一)
    array.y=2         --array 與 hash 混合 (賦值方法二)
    print(#array)    --輸出 3 (陣列長度運算子 # 不會將 hash 元素算在內)
  6. Lua 表格用法需特別注意的地方有兩點 : 一是表格當陣列使用時, 其索引是從 1 起算的 (以前的 FORTRAN 與 PASCAL 也是), 而 C 語言及其徒子徒孫都從 0 起算, 很容易搞錯. 其次是 # 運算子只能計算表格中以陣列方式儲存之元素, 不包括關聯式陣列元素. 
  7. Lua 原始設計本來並非以物件導向為目標, 它的物件導向是以 metatable (詮釋表格) 方式實現的. Lua 的物件與類別是以表格來實作, 而方法是用函式來實作. 使用 metatable 好像可以看到物件的內臟一樣, 與早期 Perl 的物件導向類似. 
  8. 以嵌入式用途為設計目標的 Lua 核心相當簡潔, 沒有基本資料型別以外的東西, 只有表格操作, 一些數學函數, 載入套件的功能, 以及包含樣式比對功能的字串, 函式庫只有檔案輸出入而已, 比起附帶大量功能的 Ruby 來說, 實在非常貧弱. 不過 Lua 很容易以 C 擴充功能, 內建的功能不過是拿來描述邏輯. 
  9. 同樣是作為嵌入式用途的 Tcl 語言, 由於附屬的 GUI 函式庫 Tk 相當成功, 反而常被拿來當作 GUI 開發語言, 相較之下, Lua 似乎還是比較能不忘初衷, 主要以嵌入其他程式為主. 
  10. Lua 直譯器的資料結構統一歸納在名為 lua_state 的結構裡, 一個 process 可以擁有許多直譯器, 因此要讓遊戲裡的每個角色都擁有個別的直譯器也是可行的. 在 multi-thread 環境下, 各個 thread 也都能擁有直譯器, 可把多核處理器的效能發揮到極限.
  11. Lua 在垃圾收集方面也下了許多功夫, 採用遞增收集演算法 (Incremental GC, 將垃圾收集工作細分, 逐一執行, 將中斷時間限縮在一定值內, 避免影響即時性) 盡量縮短中斷時間. 因為在遊戲這種對即時性要求很高的環境下, 垃圾收集機制會使遊戲中斷, 導致無法操作, 即使只有一秒也會影響玩家的感覺. 
  12. 以前 Cisco 路由器是內嵌 Tcl, 但現在的趨勢卻是 Lua.
  13. 在嵌入式應用上, Ruby 可能比不上 Lua. 雖說要將 Ruby 嵌入其他語言不是不可能 (例如 RPG 製作大師), 但卻不是 Ruby 擅長之事, 特別是 Ruby 沒有統一表現直譯器的結構, 使得一個 process 只能擁有一個直譯器, 無法達到 Lua 那樣可協調多個直譯器, 配置到多個 thread 的功能.  
  14. 隨著嵌入式硬體性能提升, 軟體複雜化, Lua 這類小規模執行環境, 為嵌入其他程式而設計的語言, 今後將有越來越多發揮的機會. 
  15. 這幾年程式語言不斷進化, 其中最劃時代的, 應該是 Chrome 內的 Javascript V8 引擎與 LuaJIT (Lua 的高速實作), 這兩個語言都具備了動態語言方便開發與令人驚異的執行速度, 甚至超越實作得較差的編譯式語言. 
松本行弘看來還蠻公道地評論了自家的 Ruby 與活躍在嵌入式世界的 Lua, 雖然口裡還是說對 Ruby 非常有信心, 但也承認了 Lua 在嵌入式方面無可取代的優勢. 我個人是覺得 Ruby 雖然以方便快速開發著稱, Lua 在這方面也不遑多讓啊! 只是還沒出現 "Lua on Rails" 這樣的框架罷了. Ruby 若不是 Ruby on Rails, 恐怕也沒有這麼風光吧!  Ruby on Rails 的特徵如下 :
  • 完全 MVC 架構
  • 排除設定檔 (XML)
  • 採用簡潔語法
  • 活用 Metaprogramming
  • 大膽擴充 Ruby 核心
除了 Lua 外, 松本也介紹並評價了 Go, Dart, Coffeescript 等語言.

Go 語言是 Google 力推的新語言 (靜態), 主要考量是要充分發揮多核心的 CPU 性能並解決 C/C++ 的缺點, 因此 Go 語言具備了設計 Concurrent 平行處理程式的能力 (電信業界採用的 Erlang 也具有設計平行處理程式的能力, 但那已是誕生於 1987 年的老語言了, 而且很奇怪, 這是個沒有迴圈的語言哩!).

Dart 語言是結構化又靈活的 Web 語言, 最大特徵是具有可省略的靜態型別. 其設計動機是為了取代 Javascript, 因為 Javascript 有下列缺點 :
  • 無法對應功能豐富的網路應用程式
  • 難以高速化
  • 無法支援多核與 GPU
  • 無法修正
但 Javascript 仍在進化, 而且擁有極多使用者社群, Dart 要達成目的恐怕有很大困難吧!

Coffeescript 的出現也是基於對 Javascript 的不滿, Javascript 語法因為太簡單, 使程式變得冗長. 而 Coffeescript 是 "為了撰寫 Javascript 而生的便利語言". Coffeescript 程式執行前要先用 Coffeescript 編譯器 (也是用 Javascript 寫的) 轉成 Javascript 程式, 然後執行此 Javascript 程式. 其語法受到 Ruby 與 Python 的強烈影響 (特別是 Python), 語法比單純過頭的 Javascript 稍微複雜. (奇怪, 居然還有人嫌語法太簡單的!)

松本也提到了與 FORTRAN, COBOL 並稱為 "古代三大程式語言" 的 Lisp, 此語言是創立者 John McCarthy 從所設計的 lambda 演算法進化而來的, 是從數學誕生的語言, 特點是以清單 (list) 結構來操作運算, 這使其廣泛應用在人工智慧領域, 其運算式充滿了許多小括弧, 閱讀起來蠻吃力的. 而我們熟知的虛擬機器與垃圾收集機制也是 Lisp 最早提出並實作出來的, 我都一直以為這是 Java 帶來的創新呢!

松本觀察程式語言的進化方向, 認為最重要的是抽象化 (黑箱化), 例如將處理過程命名就是重點之一, 抽象化的好處是程式設計者不用涉入太多語言的內部細節, 抽象化的自然演進結果之一就是物件導向, 物件就是抽象化的資料. 但也有刻意不要物件化的語言, 例如 Lisp 創立者 Paul Graham 發展的 Arc 語言就是. 松本曾對其學生出過 "預測 20 年後的語言" 這樣的作業, 大多數人預測 :

  • 能更簡單寫出程式的語言
  • 能以自然語言對電腦下令

這也是我的期望. 我認為 Lua 蠻符合第一個期待.

這本書其實還談了非常多東西, 有些比較深入探討語言設計與實作的太艱深, 看不懂 (比如閉包我也覺得非常難以駕馭). 但較淺顯的例如 Big data 常用的資料庫 NoSQL, 摩爾定律, 非阻塞式 I/O 的 node.js 等等, 蠻有可看性的, 從語言設計者的觀點來看語言的特質, 自然有不同的光景.


沒有留言 :