因為最近的一個項目,粗略了解了很多适合嵌入式的小語言,PHP,Perl,Python這些已經不能算小了,Lua發展的不錯但進化仍然緩慢,Squirrel和Lua很類似但采用了類C文法,另外還有很多有特色的小語言,包括Jx9,Pawn,Pike等等;文法都不古怪;Scheme或者Forth一類則更适合CS專業背景,喜歡抽象邏輯的開發者使用;當然這個清單中不能忽視Javascript,不隻是因為它流行,JS從Day 1開始就是為嵌入應用設計的輕量級語言,沒有太多的庫依賴,文法簡潔,如果不在意性能,它可以用非常小的解釋器實作,例如Espruino就是基于開源項目TinyJS發展出來的,很不錯。
Tcl則處在一個奇怪的位置;它現在在TIOBE的排名是100左右,基本上排在大多數程式員聽說過的所有程式設計語言之後,但它曾經很流行,在Java出現之前;在它流行的年代,計算裝置的性能很低;Unix Shell是主要的競争對手,LISP是一個美麗的泡泡;是以Tcl從一出生開始在形式上就帶有這兩種語言的特點:Shell的字元串展開,LISP的表達式優先和空格分隔符;這兩點可能讓熟悉類C文法的大多數現代程式員都不适應;
Tcl出現的年代,GUI剛起步;在各種當時還是非常封閉的平台上寫高效的GUI程式并不容易,Tk圖形庫一出現就受到了極大歡迎,它讓程式員可以非常快速的建立簡單GUI應用;時至今日,包括Python在内的很多語言,最早和預設支援的GUI Toolkit仍然是Tk,雖然界面看起來實在土不堪言;
SUN選擇了Java放棄了Tcl是對Tcl的最大打擊;之後Tcl的創始人Ousterhout将Tcl和他創辦的公司一起賣給了其他公司并離開了核心開發團隊,這門語言開始停滞發展并迅速被其他活躍的商業語言或社群語言超越;到現在你已經基本上聽不到它的名字了;
目前Tcl還在使用的一些地方包括EDA軟體的内嵌語言,某些型号的Cisco的路由器腳本,自動化測試架構,等等;
說完冗長的Tcl曆史之後我們回來扣這個題目,扣這個題目之前我們來看一句Tcl的執行個體代碼,這是Tcl裡自定義的一個小函數,叫K Combinator:
proc K {a b} {return $a}
proc是Tcl裡聲明函數(指令)的方法,K是函數名,a和b是參數變量,$a是對參數求值;是以這個指令就是你給我兩個變量(或表達式),我把第一個的值還給你;一個看起來很廢物的功能,right?
proc readfile filename { set f [open $filename] set data [read $f] close $f return $data}
這是一段從檔案中讀取資料的小程式;打開檔案,讀取資料,關閉檔案,傳回資料,不麻煩,但使用K Combinator寫會更簡單:
proc readfile filename { K [read [set f [open $filename]]] [close $f]}
方括号是Tcl運作其他指令的文法(調用子程式),$是把變量名轉換成值;K在這裡把表達式的前面部分的結果傳回;一行代碼完成。
或者我們該把K Combinator叫做文法糖?Maybe。
Tcl的Iconic特性是一句話:
Everything is a string, but not just a string.
任何一句Tcl語句,都是這樣的格式:
commandName argument1 argument2 ... argumentN
就像Shell指令;Tcl解釋器對裡面的每一部分估值、展開、或替換,并最後調用command執行;參數之間使用空格分割,剛好和我們的自然語言書寫習慣一樣,這是巧合嗎?
當然所有的計算語言差不多都這樣,如果把{}塊當作一個參數的話;但Tcl的不同之處在于:它的command是随便定義的,包括關鍵字。
IF {} THEN {} 是一個指令,while {} {}也是;事實上你可以建立任何你希望的Pattern;Tcl裡有一個合并了Command(用你習慣的語言說,函數)和keyword的庫,你随時可以定義新的Command替換舊的,事實上它并沒有真正意義上的keyword,都是可以替換的;
它的解釋器從源檔案裡忠實的一句一句取出代碼語句,到庫裡去查找比對的Pattern,然後估值、執行;
這很重要嗎?是的,魔法就在這裡。
我們現行的計算語言,如果時間翻過去幾十年回頭看,它将是一種恥辱,因為它在讓人,學習計算機的思維方式,而不是相反;
曾經有一個印度詩人,因為某著名英國文人的大力推薦風靡全球;那個英國人把印度人用印度某地方言寫的詩都翻譯成英文,據說很美,有可能影響過徐志摩、張愛玲、和青春期的張大錘;
但事實上這個英國人出于某種不為人知的動機利用自己的文學功底對這些詩做了高度粉刷,那個印度人使用的方言,據說就隻有大約2000個詞彙,你能想象出一個封閉村落的原始生活裡提煉出來的語言能有多大和多豐富的表現力?
在數學上,CPU的指令字,或者程式設計語言裡的基本結構(Construct)加關鍵字,張開的狀态空間可以是無窮的,富有變化的,和可發展的;但這是數學,不是人性,不是人類語言;
人類的語言是,我推薦你看看MIT試驗心理學專家、認知科學家、語言學家Steven Pinker的很有影響力的著作:Language Instinct,語言本能;這本書告訴我們人類是如何思考的,結論性的概況是:
We Think in Pattern.
你看這篇文章,想想我們說的話,我們有文法,有主謂賓定狀補結構,我們有修飾語,我們能表達What, Why, When, Where, How;而對比計算語言呢?計算語言裡是if then else do while go,你兩個巴掌的手指頭就數得過來;
是計算語言要表達的東西不夠豐富嗎?不;他們表達的東西夠豐富,但不是通過語言的文法結構來表達,而是通過個體程式員發明的函數名,變量名,以及程式設計大拿們經過工程實踐總結出來的Pattern來實作,四人幫的設計模式就是代表作品;
但如果是Tcl,你可以自己去發展這門語言,你可以建立Adapter,Factory,Mediator,State Machine,Event Handler,等等等等,在文法結構層面,建立新的Construct,讓這些東西就像上面的K Combinator這個小小的Trick一樣,成為你建立的程式設計語言的一部分,直覺、簡潔、緊湊、流暢、自我描述,線性擴充;
這可以非常好的解釋為什麼一些大型軟體公司仍然在積極的使用Tcl,它可以非常快速簡單的把Tcl變成它獨有的Domain Specific Language,讓它易讀易寫;事實上,還在使用的Tcl版本除了很少的基礎功能相同之外,每家公司都加入了很多自己需要的特性,用在不同領域的Tcl也完全不一樣;它很像Linux核心的使用;
不管一個程式設計語言在發展之初有多麼龐大的野心,注入多少商業和非商業的開發資源,最終它一定不可能面面俱到,它一定或多或少的在某些領域更擅長,在另外一些地方完全無法使用;是以仿照那句著名的諺語:一切曆史都是當代史,我們可以說:
一切程式設計語言都是領域特定語言(Domain Specific Language);
那麼當你需要定制化一門語言的時候,最好的方式并不是采用通用程式設計語言裡最精簡的語言部件去繁瑣的表達你的應用,而是把這門語言直接在文法層面适應到你的要求;上帝的語言是抽象的,垂直分層的,原子有實體,分子有化學,生物有DNA;但人類的語言是線性擴張的,有豐富的文法結構,語彙,修飾方法,律師有律師的語言,會計有會計的,軍人有軍人的,這才是我們的語言本能;In short:
語言應該能自定義Construct,表達Pattern。
這正才是人類語言的發展方式;
說Tcl是最好的語言,當然很多人不會同意,好有很多度量方式,可能是性能,可能是功能,涵蓋的範圍,适合的場景;
但是如果你從語言的角度出發,而不是從程式設計的角度出發,Tcl提供的自定義文法結構的能力,是絕大多數其他程式設計語言做不到的;從這個意義上說,它抓住了語言的核心,它有潛力完成Arduino想做到的、雖然取得了長足的進步但是并沒有真正完成的任務:讓計算裝置的行為變得真正的accessible,讓程式設計工作更加descriptive。
當然也許不是Tcl,也許是新發展出來的其他語言,但這是正确的方向和問題的核心,年輕人,計算領域正在等着你的最後努力,讓它變得更加美好,進入理想世界。