來自C#之父的編程語言趨勢預測
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
程序設計離不開編程語言,但是編程語言在國內的大環境中似乎一直是個二等公民,許多人也認為,語言的討論完全是不入流的,但其實編程語言與工具、框架或開發方法等一樣,都對生產力有著重要的影響。
[p] 事實上,語言的發展歷史比其他方面更為悠久,并且在過去十幾年,甚至最近幾年中都依然在不斷的碰撞和演變。期間一些新的語言誕生了,而另一些在當時看來陽春白雪的語言和編程范式也重新獲得了重視。[/p] [p] anders hejlsberg是微軟的technical fellow,擔任c#編程語言的首席架構師,也參與了.net framework、vb.net和f#等語言的設計與開發,《delphi與c#之父:技術理想架構開發傳奇》一文詳細介紹了anders hejlsberg在微軟的編程傳奇之路。[/p] [p] 幾個月前,anders在比利時techdays 2010及荷蘭devdays 2010分別作了一場演講,闡述了他眼中的編程語言的發展趨勢及未來方向,本文便對他的觀點進行了總結。[/p] [p] 大約25~30年前,anders開發了著名的turbo pascal,這是一套集語言、編譯器及開發工具于一體的產品,也是anders進入編程語言領域的起點。anders談到,當年turbo pascal所用的z-80和如今的計算機已經不可同日而語。與那時相比,如今的機器已經有大約10萬倍的外部存儲容量,1萬倍的內存大小,cpu速度也有大約1000倍的提高。但是,如果我們比較如今的java代碼及當年的pascal代碼,會發現它們的差別其實并不大。[/p] [p] anders認為編程語言的發展非常緩慢,期間當然出現了一些東西,例如面向對象等,但是遠沒有好上1000倍。事實上,近幾十年來的努力主要體現在框架及工具等方面(如圖1)。例如.net framework里有超過一萬個類和十萬個方法,與turbo pascal相比的確有了超過1000倍的增長。類似的,現在的ide包含了無數強大的功能,例如語法提示、重構、調試器等。與此相比,編程語言的改進的確很不明顯。[/p] [p] 在過去50~60年的編程歷史中,編程語言的抽象級別不斷提高,人們都在努力讓編程語言更有表現力,這樣就可以用更少的代碼完成更多的工作。我們一開始使用匯編,然后使用面向過程的語言(如pascal和c),然后是面向對象語言(如c++),隨后便進入了托管時代,語言運行于受托管的執行環境上(如c#和java),它們的主要特性有自動垃圾收集、類型安全等。anders認為這樣的趨勢還會繼續下去,還會有抽象級別越來越高的語言。另一方面,編程語言往往都傾向于構建于現有的工具上,而不會從頭寫起。現在出現的編程語言,例如f#、scala和clojure等,都是基于現有框架構建的,每次從頭開始的代價實在太高。[/p] [p] 在anders眼中,如今影響力較大的趨勢主要有三個(如圖2),分別是聲明式的編程風格(包括領域特定語言、函數式編程)、動態語言(最重要的方面是元編程能力)以及多核環境下的并發編程。此外隨著語言的發展,原本常用的面向對象語言、動態語言或是函數式等邊界也變得越來越模糊,例如各種主要的編程語言都受到函數式語言的影響。因此,多范式程序設計語言也是一個愈發明顯的趨勢。[/p] [p] [b]聲明式編程與dsl[/b][/p] [p] 目前常見的編程語言大都是命令式(imperative)的,例如c#、java或c++等。這些語言的特征在于,代碼里不僅表現了“做什么(what)”,而更多表現出“如何(how)完成工作”這樣的實現細節,例如for循環、i += 1等,甚至這部分細節會掩蓋我們的最終目標。[/p] [p] 在anders看來,命令式編程通常會讓代碼變得十分冗余,更重要的是由于它提供了過于具體的指令,這樣執行代碼的基礎設施(如clr或jvm)沒有太多發揮空間,只能老老實實地根據指令一步步地向目標前進。例如,并行執行程序會變得十分困難,因為像“執行目的”這樣更高層次的信息已經丟失了。因此,編程語言的趨勢之一,便是能讓代碼包含更多的“what”,而不是“how”,這樣執行環境便可以更加聰明地去適應當前的執行要求。[/p] [p] 關于聲明式的編程風格,anders主要提出了兩個方面,第一個方面是dsl(domain specific language,領域特定語言)。dsl不是什么新鮮的玩意兒,我們平時經常接觸的sql、css、正則表達式等都屬于dsl。有的dsl可能更加專注于一個方面,例如mathematica、logo等。這些語言的目標都是特定的領域,與之相對的則是gppl(general purpose programming language,通用目的編程語言)。[/p] [p] martin fowler將dsl分為外部dsl和內部dsl兩種。外部dsl有自己的特定語法、解析器和詞法分析器等,它們往往是一種小型的編程語言,甚至不會像gppl那樣需要源文件。與之相對的則是內部dsl。內部dsl其實更像是種別稱,它代表一類特別api及使用模式。[/p] [p] xslt、sql等都可以算作是外部dsl。外部dsl一般會直接針對特定的領域設計,而不考慮其他方面。james gosling曾經說過:每個配置文件最終都會變成一門編程語言。一開始你可能只會用它表示一點點東西,慢慢地你便會想要一些規則,而這些規則則變成了表達式,后來你可能還會定義變量,進行條件判斷等,而最終它就變成了一種奇怪的編程語言。這樣的情況屢見不鮮。現在有一些公司也在關注dsl的開發。[/p] [p] 例如以前在微軟工作的charles simonyi提出了intentional programming的概念,還有jetbrains公司提供了叫做mps(meta programming system)的產品。最近微軟也提出了自己的oslo項目,而在eclipse世界里也有xtext,所以如今在這方面已經有不少嘗試。由于外部dsl的獨立性,在某些情況下也會出現特定的工具,輔助領域專家或是開發人員編寫dsl代碼。還有一些dsl會以xml方言的形式提出,利用xml方言的好處在于有不少現成的工具可用,這樣可以更快地定義自己的語法。[/p] [p] 內部dsl往往只代表一系列特別的api及使用模式,例如linq查詢語句及ruby on rails中的active record聲明代碼等。內部dsl可以使用一系列api來“偽裝”成一種dsl,利用一些流暢化的技巧,例如像jquery那樣把一些方法通過“點”連接起來,而另一些也會利用元編程的方式。內部dsl還有一些優勢,例如可以訪問語言中的代碼或變量,以及利用代碼補全、重構等母語言的所有特性。[/p] [p] dsl的可讀性往往很高。例如,要篩選出單價大于20的產品,并對所屬種類進行分組,降序列出每組的分類名稱及產品數量。如果是用命令式的編程方式,可能是這樣的:[/p] var groups = new dictionary(); foreach (product p in products) { if (p.unitprice >= 20) { if (!groups.containskey(p.categoryname)) { grouping g = new grouping(); g.name = p.categoryname; g.count = 0; groups[p.categoryname] = g; } groups[p.categoryname].productcount++; } } var result = new list(groups.values); result.sort(delegate(grouping x, grouping y) { return x.count > y.count ? -1 : x.count < y.count ? 1 : 0; }); [p] 顯然這些代碼編寫起來需要一點時間,且很難直接看出它的真實目的,換言之,“what”幾乎完全被“how”所代替了。這樣,一個新的程序員必須花費一定時間才能理解這段代碼的目的。但如果使用linq,代碼便可以改寫成:[/p] [p] var result = products where(p => p.unitprice >= 20) groupby(p => p.categoryname) orderbydescending(g => g.count()) select(g => new { name = g.key, count = g.count() });[/p] [p] 這段代碼更加關注的是“how”而不是“what”,它不會明確地給出過濾的操作方式,也沒有涉及到創建字典這樣的細節。這段代碼還可以利用c# 3.0中內置的dsl,即linq查詢語句來改寫:[/p] [p] var result = from p in products where p.unitprice >= 20 group p by p.categoryname into g orderby g.count() descending select new { name = g.key, count = g.count() };[/p] [p] 編譯器會簡單地將linq差距語句轉化為前一種形式。這段代碼只是表現出最終的目的,而不是明確指定做事的方式,這樣便可以很容易地并行執行這段代碼,如使用pinq則幾乎不需要做出任何修改。[/p] [p] [b]函數式編程[/b][/p] [p] anders提出的另一個重要的聲明式編程方式便是函數式編程。函數式編程歷史悠久,如當年的lisp便是函數式編程語言。除了lisp以外還有其他許多函數式編程語言,如apl、haskell、ml等。函數式編程在學術界已經有過許多研究,大約在5~10年前許多人開始吸收和整理這些研究內容,想要把它們融入更為通用的編程語言。現在的編程語言,如c#、python、ruby、scala等,都受到了函數式編程語言的影響。[/p] [p] 使用命令式編程語言寫程序時,我們經常會編寫如x = x + 1這樣的語句,此時我們大量依賴的是可變狀態,或者說是變量,它們的值可以隨程序運行而改變,可變狀態非常強大,但隨之而來的便是“副作用”問題,例如一個無需參數的void方法,它會根據調用次數或是在哪個線程上進行調用對程序產生影響,它會改變程序內部的狀態,從而影響之后的運行效果。而在函數式編程中則不會出現這個情況,因為所有的狀態都是不可變的。事實上對函數式編程的討論更像是數學、公式,而不是程序語句,如x = x + 1對于數學家來說,似乎只是個永不為真的表達式而已。[/p] [p] 函數式編程十分容易并行,因為它在運行時不會修改任何狀態,因此無論多少線程在運行時都可以觀察到正確的結果。假如兩個函數完全無關,那么它們是并行還是順序執行便沒有什么區別。[/p] [p] 當然,現實中的程序一定是有副作用的,例如向屏幕輸出內容,向socket傳輸數據等,因此真實世界中的函數式編程往往都會考慮如何將有副作用的代碼分離出來。函數式編程默認是不可變的,開發人員必須做些額外的事情才能使用可變狀態或是危險的副作用,與之相反,c#或java必須使用readonly或final來做到這一點。此時,使用函數式編程語言時的思維觀念便會有所不同。[/p] 該文章在 2010/8/18 13:47:16 編輯過 |
關鍵字查詢
相關文章
正在查詢... |