你是否曾對軟件編程棧的混亂感到過困惑?錯綜復雜和臃腫的解決方案無處不在,但無論是在當代軟件的構建還是維護環節,很少有架構師和程序員能真正意識到由此帶來的成本。Red的存在正是為了反擊這種復雜性,這是它最主要的設計目的。沒錯,在現代軟件世界中,“簡單”的工具和簡單的解決方案仍是可以企及的。這是個所有人都竭力掖藏的行業秘密(這可不是什麼好事兒),但自1997年以來,解決方案其實已由Rebol編程語言給出。Red繼承自Rebol譜系,並嘗試將Rebol原先的使用范圍加以大大拓展。Red是個雄心壯志的項目,它想要成為首個全棧編程語言。以下是其主要特性和設計目標:
Red語言,正如Rebol一樣,依靠獨特的途徑來編程,與目前主流語言的主張有所不同。以下是JSON發明者Douglas Crockford對Rebol語言發表的評論:
Rebol是一種更具現代性的語言,但也有些與Lisp語言非常相似的思想蘊涵在內,主要體現在它也是完全建立在先進行數據表示、爾後將其作為程序執行起來這樣的基礎之上。但它擁有更豐富的語法材料。Rebol是一種才華橫溢的語言,它本該更普及一些,現在卻並未如此,這可引為憾事。
Red和Rebol的設計目標是要盡可能地提供表達能力,同時保持源代碼的高可讀性,事實上它們的源代碼很接近自然語言。2013年的一項將程序設計語言按表達能力排序的研究項目中,Rebol語言名列第三,僅次於Augeas和Puppet這兩種DSL(http://goo.gl/qyPXx)。這有力地表明,在所有通用型程序設計語言中,Rebol是表達能力最強的一個。Red和Rebol都遵守務實設計原則,沒有哪部分是拍腦袋的決定,或是“為了不同而不同”。每個語法或語義背後,都有明確的設計理由。
Red和Rebol如何在企及如此水平的效率的同時,又保持“簡單”呢?根本的原因之一,是因為這兩者都既是一種數據格式,又是一門程序設計語言。此特性繼承自Lisp,特別是s-表達式概念。Lisp中也建立了Red和Rebol所依賴的元編程模型。它們都是同像語言,所有的代碼都表示為數據。值存儲在塊中(使用方括號記法:[……]),即普通列表,這和Lisp的記法一樣,但它們不要求函數調用的那對括號,這就使得代碼看上去更像自然語言。函數在默認情況下采用前綴記法,然而,Red和Rebol也支持針對數學和布爾運算符采用中綴表示法,這為代碼閱讀提供了便利。采用這種純元編程進行程序設計,反射、熱補丁、甚至即時編碼這些語言特性都能得到內建支持,不需要學習任何新API,也無須特別的庫支持,只使用基本函數進行數據操作即可。以下是使用Red語言REPL的示例:
$ red red>> code: ["hello"] == ["hello"] red>> insert code 'print == ["hello"] red>> code == [print "hello"] red>> do code hello red>> code/2: 123 == 123 red>> code == [print 123] red>> append code [+ 1] == [print 123 + 1] red>> do code 124 red>> length? code == 4 red>> type? code/1 == word! red>> foreach value code [probe type? value] word! integer! word! integer!
如君所見,該語言的基本構件如下:
這些就是用來表示數據、構建表達式、定義函數、創建對象、以及構造更復雜數據類型的基本構件。符號都是一等(first-class)值,也是一種更自然也更有效的字符串替代品,這對於很多用例都適用,尤其是涉及查找操作時就更能說明問題,因為符號可以以O(1)的效率比較,而字符串則要求O(n)。符號不區分大小寫,所以print、Print和PRINT是等同的,人類語言不也是這樣嗎?
Rebol或Red程序源代碼通常都是UTF-8輸入字符串,它們都會被LOAD化處理。LOAD是原生的核心功能,能夠將任何字符串變換成包含在一個塊中的內存二進制格式。塊將值存儲至128比特的相鄰單元內。值主要分為標量值(其尺寸固定)或序列值(用戶可以向其中添加/刪除數據)。數字、日期、元組和值對是標量值的,而值塊、字符串、URL、路徑、文件和標簽是序列值的示例。標量值通常可以置入單個存儲單元,而序列值則需要額外內存。
因此,塊是帶有垃圾回收的內存管理器進行保留和回收的主要分配單位。Rebol采用經典的stop-the-world標記和清理垃圾回收算法,而Red則依賴於stop-the-thread分代壓縮垃圾回收算法(尚未完全實現),交替進行部分或全部遍掃。Red將擴充其垃圾回收算法,在未來實現增量回收,以使得它可以用來開發實時應用,例如60-FPS的街機游戲。
一旦源代碼被加載入內存,變成一個值塊,它就只是純粹的數據。默認地,在遍掃源代碼,使之變換成Red或Rebol二進制文件時,就會對值進行計算。如果是用Rebol語言,加載的塊會被解釋執行;如果是用Red語言,它們就將被編譯成本地代碼,但目的都是為了完成計算。然而,值塊以何種方式進行解釋,則是依賴於語境的。解釋方式有如下幾種:
默認的(非純的)、以函數式方式解釋的語言(即我們通常所謂的“代碼”),是一種面向表達式的語言,它帶有很簡單的語義規則:
因此,浮在語言上方的函數層可以很容易地被任何自定義計算體取代(例如,撰寫一個類似於Prolog的解釋器將非常簡單)。這使得Red和Rebol具備了極好的可延展性,十分容易適應任何你可能的需要。這種靈活性是DSL得以成為一種自然的方式解決一些計算任務,通過提供特定領域的微語言對於給定的任務實現優化的基礎。Red和Rebol憑借這種力量,在核心語言和標准庫中廣泛地使用了嵌入式DSL:
使得嵌入式DSL的創建容易、便捷的因素有:
下面是一些在Rebol2中的GUI DSL的示例:
按一個大小為100×100的紅色按鈕,以打開一個窗口:
>> view layout [button "Hello" red 100x100]
顯示會觸發一個動作的按鈕:
>> view layout [button "Hi" [print "Hello!"]]
顯示按鈕,打印一個字段的內容:
>> view layout [ in: field 200 button "Print" [print in/text] ]
Red語言中的一個Parse DSL示例:
red>> digit: charset "0123456789" red>> parse "hello 888 world" [ some [copy n some digit | skip] ] red>> n == "888"
由於想要解決Rebol語言的一些不足之處,Red語言應運而生。這些不足包括:
Red在解決以上大部分問題時都采用了創新設計:嵌入一個低層次的程序設計語言,它可以直接編譯為本地代碼,即Red/System。它采用Red語法(值仍為塊的形式),但與C類似,語義級別更低。它是靜態類型語言,並僅提供少數幾種數據類型:integer!、float!、float32!、byte!、logic!、pointer!、struct!,以及function!。它算支持指針算術,這賦予了它近於C的力量。標准庫也非常簡約,所以純Red/System編譯後的代碼尺寸非常小,下面這個程序:
Red/System [ Title: "Hello World app" ] print "Hello World!"
編譯後通常小於10KB。
Red程序被編譯成Red/System代碼,並與Red標准庫鏈接,而Red標准庫則大部分采用Red/System,少部分用Red自身寫就。該庫目前未壓縮體積約為200KB。我們的目標是在發行1.0版時,將其控制在500KB左右。
Red與Red/System緊密集成,有數種方法在Red語言中調用或內嵌Red/System代碼。其中最有用的一種是“常規”函數類型,它允許定義一個Red函數,其函數體為純Red/System代碼。Red語言會為你自動完成傳遞參數和返回值的裝箱/拆箱操作。有了Red/System的幫助,Red語言便可以解決從硬件到DSL的任何抽象層次,從而成為真正的首個全棧編程語言。
因此,Red依靠Red/System工具鏈來生成可執行文件。該編譯器和鏈接器目前支持的目標平台包括:
除此之外,Red語言還依靠橋接技術,以訪問像Java那樣的虛擬機,尤其是提供對Android的支持。事實是,目前已可通過Red/Java橋,采用JNI技術來從Red程序遠程控制Java和Android API,並獲取事件返回。2014年晚些時候,我們還計劃推出Red/Obj-C橋,以訪問iOS和Cocoa API。
盡管後端和文件格式的組合可能性繁多,但確定選用何種目標平台的Red配置文件卻短小精悍。這就使得交叉編譯變得非常簡單,例如:
從Linux或Mac平台上生成一個Windows平台的可執行文件:
$ red -t Windows hello.red
從Windows平台上生成一個Raspberry Pi平台的可執行文件:
$ red -t Linux-ARM hello.red
以下是關於這些目標平台的定義:
Windows [ OS: 'Windows format: 'PE target: 'IA-32 type: 'exe sub-system: 'GUI ] Linux-ARM [ OS: 'Linux format: 'ELF target: 'ARM type: 'exe base-address: 32768 ; 8000h dynamic-linker: "/lib/ld-linux.so.3" ]
Red語言的開發工作仍然任重道遠。目前它的工具鏈部分使用Rebol2完成自舉(指Red語言和Red/System編譯器+鏈接器)。由於需支持JIT編譯動態生成的Red/System代碼,Red語言必須有自承載能力,所以所有工具鏈需要在1.0版之後用Red語言重寫。新的工具鏈將能生成真正包含全部特征的Red語言,並同時為兩種編譯器提供優化層,生成更小更快的代碼。目前,Red/System的性能實測下來大約比C慢4~6倍,這對於未經優化的本地代碼而言已經不錯了。
性能還會隨著即將到來的並發支持而得到提升。今年晚些時候,Red語言會提供一個完全異步I/O接口,以及M:N線程模型(M個輕量級線程,分發到N個OS線程中去)。根據目前的計劃,更高級別的抽象會使用Actor模型,這是為了實現簡單而高效的共享狀態同步。從用戶的角度看,Actor也只是個一等的數據類型,和使用任何其他對象都差不多,很少或者根本沒有額外的語法負擔。然而,考慮到Go語言的goroutine模型的擁趸之多,我們仍在斟酌各種選項以決定使用Red語言的最佳模式。
Red語言是一個開源項目,采用BSD許可證。作為語言的作者,我全職工作於此業已三年。項目資金來自用戶和支持者捐款。自2011年首次發布以來,得到的支持已然令我難以置信。開發者對於Red語言可提供的一切感到驚喜,尤其是那些了解Rebol語言能力的用戶。Red語言對於他們中的大多數人,包括我自己,都是夢寐以求的工具。Red語言再次將樂趣帶回了程序設計,將復雜性拒之門外,讓程序員們再次感覺控制在握,就像那曾經經歷的8位機時代。
由於Red語言將具備強大的Android支持,又格外適合新入行的程序員,我們希望它能夠被世界上最大的Android市場——中國所接受。在中國,程序員數量巨大,而最有才華的Red語言貢獻者中,就有一位是來中國上海的程序員謝晴天。
你可以從http://www.red-lang.org/p/download.html下載Red二進制文件。可執行文件只有半兆字節,包含了整個工具鏈,其中包括Red語言和Red/System編譯器,一個帶有可選控制台的解釋器,還有目前支持30余種數據類型的整個標准庫。你也可以嘗試雖然老舊一些,但比較完整的Rebol2解釋器,尤其是在測試GUI DSL時。