編程語言的歷史早於真正意義的計算機的出現。19世紀就有"可編程的"織布機和鋼琴彈奏裝置出現,它們都是領域特定語言(DSL)的樣例。javascript
編程語言發展的編年史java
咱們一個統計出來256種編程語言,固然,這麼多的語言中只有一些是經常使用的或實用的。python
1951 – Regional Assembly Language程序員
1952 – Autocode正則表達式
1954 – IPL (LISP語言的祖先)算法
1955 – FLOW-MATIC (COBOL語言的祖先)數據庫
1957 – FORTRAN (第一個編譯型語言)編程
1957 – COMTRAN (COBOL語言的祖先)api
1958 – LISP瀏覽器
1958 – ALGOL 58
1959 – FACT (COBOL語言的祖先)
1959 – COBOL
1959 – RPG
1962 – APL
1962 – Simula
1962 – SNOBOL
1963 – CPL (C語言的祖先)
1964 – BASIC
1964 – PL/I
1966 – JOSS
1967 – BCPL (C語言的祖先)
1968 – Logo
1969 – B (C語言的祖先)
1970 – Pascal
1970 – Forth
1972 – C
1972 – Smalltalk
1972 – Prolog
1973 – ML
1975 – Scheme
1978 – SQL
1980 – C++ (既有類的C語言,改名於1983年7月)
1983 – Ada
1984 – Common Lisp
1984 – MATLAB
1985 – Eiffel
1986 – Objective-C
1986 – Erlang
1987 – Perl
1988 – Tcl
1988 – Mathematica
1989 – FL
1990 – Haskell
1991 – Python
1991 – Visual Basic
1993 – Ruby
1993 – Lua
1994 – CLOS (ANSI Common Lisp的一部分)
1995 – Java
1995 – Delphi (Object Pascal)
1995 – Java
1995 – PHP
1996 – WebDNA
1997 – Rebol
1999 – D
2000 – Action
2001 – C#
2001 – Visual Basic .NET
2002 – F#
2003 – Groovy
2003 – Scala
2007 – Clojure
2009 – Go
2011 – Dart
從1951年2014年,人類一共發明了256種編程語言,每一種語言的出現都帶有某些新特徵。編程語言不斷的在革新,很快就會有超出這個清單的新編程語言出現
1801- Joseph Marie Jacquard用打孔卡爲一臺織布機編寫指令,在掛毯上織出了「hello, world」字樣。當時的reddit網友對這項工做的反響並不熱烈,由於它既缺乏尾遞歸調用,又不支持併發,並且甚至都沒有注意在拼寫時恰當地區分大小寫。
Jacquard織布機是第一臺可進行程序控制的織布機。用打孔卡進行編程的概念,直到電子計算機被髮明出來以後仍然被普遍運用。
最先的(出自K&R C)打印的是全小寫的字符串:"hello, world"。
在許多英文技術社區裏,不正確地使用大小寫發貼會被視做是小白的行爲。(如把「Python」拼做「python」,把「FreeBSD」拼做「freebsd」,把「Qt」拼做「QT」)
1842- Ada Lovelace寫了世界上第一個程序。她的努力只遇到了一點點小小的麻煩,那就是:實際上並無任何計算機可以用來運行她的程序。後來的企業架構師們從新吸取了她的這個技能,用來學習如何更好地使用UML進行編程。
Ada Lovelace爲Charles Babbage的分析機寫了一個計算伯努利數的算法實現,所以被後世公認爲是世界上第一個程序員。實際上,分析機因爲其設計思想過於先進,在當時根本沒有 被製造出來。(Babbage的分析機通常被認爲是現代電子通用計算機的先驅)
諷刺如今的某些「軟件架構師」頂多只會紙上談兵地畫畫UML。
1936- Alan Turing發明了世間一切程序語言的最終形態,但很快他就被英國軍情六處「請」去當007了,以致於他根原本不及爲這些語言申請專利。
與通用圖靈機(Universal Turing machine)等價的語言被稱爲圖靈完備的(Turing completeness),它定義了「什麼樣的語言能夠被稱做是程序語言」。
二戰期間Turing曾祕密地爲英國軍方工做,破解德軍的Enigma密碼機,並在戰後被授予大英帝國勳章。但這項事實直到多年之後才向公衆公開。
1936- Alonzo Church同時也發明了世間一切程序語言的最終形態,甚至作得更好。但他的λ演算被絕大部分人忽視了,由於它與C語言「不夠像」。儘管存在着這樣的批評,但事實上,C在當時尚未被髮明出來。
Church是Turing在Princeton的博士生導師,他在λ演算方面的工做先於Turing指出了不存在一個對可斷定性問題的通用解法,這後來證實和Turing針對停機問題提出的圖靈機模型是等價的。即著名的。
說Church「甚至作得更好」,由於λ演算爲後世全部的函數式語言提供了理論基礎。
如今一種常見的關於函數式編程的批評就是:「它們與C語言不夠像」。
1940年代- 一些直接採用佈線和開關來進行程序控制的「計算機」出現了。工程師們當時這麼作,聽說是爲了避開「用空格仍是用製表符縮進」這樣的論戰。
聽說當時負責設計ENIAC的工程師中間曾經發生過這樣的爭論:
空格比製表符好。
製表符比空格好。
4個空格比8個空格好。
什麼?用2個空格的通通燒死。
關於這臺具備里程碑意義的人類史上第一臺電子計算機ENIAC上應該預裝何種編輯器,工程師們還發生過這樣的爭吵:
Vim比Emacs好!
Emacs比Vim好!
強烈推薦Sublime Text。
你丫用編輯器的都是找虐,IDE纔是王道。
沒錯,要用就用世界上最好的公司微軟開發出來的世界上最好的IDE:Visual Studio。
我早就看透了無謂的編輯器論戰什麼的了,我要告訴樓上吵架的,大家全都是傻逼!
最後,工程師們一致決定使用佈線和開關來爲他們即將發明的計算機進行編程,機智地避開了全部這些無謂的爭吵,最終齊心合力創造出了人類歷史上第一臺電子計算機:ENIAC。(鼓掌
(圖:兩位ENIAC程序員在運用敏捷開發方法進行愉快的結對編程。「自從拋棄伴隨我多年的Emacs和HHKB Pro、改用佈線和開關進行編程以後,個人左手小指麻痹奇蹟般地痊癒了。」其中一位接受採訪時如是說。另外一位則表示:「新的編程方式讓曾經專一頸椎病20 年的我獲得了完全的康復,不用再成天盯着顯示屏,身心同時獲得了極大的放鬆,值得大力推廣!」)
1957- John Backus和IBM發明了FORTRAN語言。關於IBM或FORTRAN並無什麼特別可笑的地方。除了,寫FORTRAN程序的時候不繫藍領帶將被編譯器視做是一個syntax error。
藍領帶、白襯衫、深色西裝彷佛是IBM公司20世紀經典的dress code。
早期FORTRAN(FORTRAN 77)對程序書寫格式的要求那是至關嚴格。(例如,蛋疼的固定格式縮進)
1958- John McCarthy和Paul Graham發明了LISP。因爲冷戰期間的戰略括號資源儲備所形成的巨大成本,LISP從未流行過。儘管欠缺足夠的流行度,LISP(如今叫作 「Lisp」,有時叫「Arc」)仍然被視做一門有影響力的語言,在關鍵的算法思想諸如遞歸(recursion)和提高逼格 (condescension)上尤其典範。
LISP發明的那一年Paul Graham其實尚未出生。聽說是由於某本叫作《Haste and Waste》的僞程裝黑聖典實在太有名了,以致於許多編程小白們把寫這本書的傳奇人物同Lisp之間畫上了等號。
提高逼格確實是一種與遞歸調用一樣關鍵的算法思想。嗯,你懂的。
1959- 在輸掉了和L. Ron Hubbard之間的一場打賭以後,Grace Hopper和其餘幾個抖S發明了所謂的「面向Boilerplate的全大寫化語言(Capitalization Of Boilerplate Oriented Language,COBOL)」。多年之後,因爲一些被誤導的、性別歧視主義者對Adm. Hopper關於COBOL的工做的報復,在Ruby技術會議上不時會看到一些厭女主義乃至仇視女性的材料出現。
L. Ron Hubbard是山達基教(Scientology)的創始人,二戰期間曾與Grace Hopper一樣供職於美國海軍。(尚不清楚這兩人之間有無其餘聯繫)
COBOL語言以代碼極其冗長和通篇大寫字母的書寫風格而聞名。
Adm. Hopper:Grace Murray Hopper女士的軍銜是Rear Admiral Lower Half,即美國海軍准將。
1964- John Kemeny和Thomas Kurtz創造了BASIC,一個爲非計算機科學家設計的非結構化的程序語言。
1965- Kemeny和Kurtz兩人goto到了1964。
調侃BASIC語言對行號和goto的無節制濫用。
1970- Guy Steele和Gerald Sussman創造了Scheme。他們的工做致使了一系列以《Lambda之究極(Lambda the Ultimate)……》爲標題開頭的論文發表,並在《Lambda之究極廚房神器》這一篇中達到了最高潮。以這篇論文爲基礎,開始了一個終年累月的、收 視率究極失敗的晚間電視購物節目。Lambda們由於其概念相對難以理解而被大衆所忽視,直到將來的某一天,Java語言終於讓它們變得有名了起來。經過 不包含它們這件事情。
1970- Niklaus Wirth創造了Pascal,一個過程式的語言。很快就有人開始聲討Pascal,由於它使用了相似「x := x + y」這樣的語法,而不是更爲人熟知的類C語法「x = x + y」。儘管存在着這樣的批評,而事實上當時C尚未被髮明出來。
1972- Dennis Ritchie發明了一把射擊時能同時向前和向後兩個方向發射子彈的絕世好槍。但他對此發明形成的致死和終身殘疾數量感到還不夠滿意,因此他又發明了C語言和Unix。
翻譯君:……
1972- Alain Colmerauer設計了邏輯編程語言Prolog。他的目標是創造一個具備兩歲小孩智商的程序語言。爲了證實他成功達到了這個目標,他展現了一個Prolog程序,它對於每條查詢都會機智地給出相同的回答:「No」。
1973- Robin Milner創造了ML,一個創建在M&M類型理論基礎上的語言。由ML衍生而來的SML加上了一套形式語義的規範。當被要求給這個形式語義自己 書寫一套形式語義時,Milner的腦子爆掉了。其餘ML家族的著名語言還包括OCaml,F#,和,Visual Basic。
1980- Alan Kay創造了Smalltalk併發明瞭「面向對象」這個詞。當被問到它的含義時,他回答道:「Smalltalk程序自己就是對象。」當被問到對象是由 什麼組成時,他回答到:「對象。」當再一次被問到這個問題時,他說「看,它從裏到外都是對象。直到你抽出一隻烏龜。」
Smalltalk的設計從很大程度上受到了的影響。
1983- 爲了記念偉大的先輩程序員Ada Lovelace那可以寫出永遠也沒法被執行的代碼的彪悍技能,Jean Ichbiah和美國國防部創造了Ada語言。儘管缺少證據顯示有任何重要的Ada程序曾經被完成過,歷史學家仍然確信Ada是個成功的公益項目,它讓數 以千計的國防承包商免於淪落爲與黑幫爲伍。
Ada曾經是美國國防部指定的嵌入式計算機系統惟一開發語言,在其研發上耗資巨大。(國防承包商們因而不用靠販賣軍火給黑幫來維持生計了)
雖然Ada的整型範圍溢出檢查失敗致使弄壞了歐空局的一枚Ariane 5運載火箭,不過美國國防部發言人對此表示:關我P事。
1983- Bjarne Stroustrup把他所據說過的一切都試圖嫁接到C上,創造出了C++。最後獲得的語言是如此地複雜,以致於程序必須被送到將來去讓「天網」人工智能 進行編譯。編譯時間難以容忍。天網開展這項服務的動機仍然鮮爲人知,但來自將來的發言人說道:「沒什麼好擔憂的,寶貝。」帶着一口奧地利腔的機械口音。有 一些來自坊間的推測,所謂的天網只不過是個自命不凡的緩衝區溢出而已。
1986- Brad Cox和Tom Love創造了Objective-C,宣稱「該語言完美地結合了C的內存安全性與Smalltalk的神奇效率」。如今的歷史學家懷疑這兩人實際上是誦讀障礙症患者。
「C的內存安全性十分好」。
Smalltalk編譯出來的程序以低效緩慢著稱。
1987- Larry Wall在電腦前打了個盹,Larry Wall的腦門子壓到了鍵盤上。醒來以後,Larry Wall深信 ,在Larry Wall的顯示器上出現的神祕字符串並不是是隨機的,那是某種編程語言之程序樣例的神諭。那必是上帝要他的先知,Larry Wall,去設計的。Perl語言就此誕生了。
1990- 一個由Simon Peyton-Jones、Paul Hudak、Philip Wadler、Ashton Kutcher和藹待動物組織(PETA)組成的委員會創造了Haskell,一種純函數式的、非嚴求值的語言。Haskell因爲使用了Monad這種 較費解的概念來控制反作用而遭到了一些批評意見。Wadler試圖平息這些質疑,他解釋說:「一個單子(Monad)說白了不過就是自函子範疇上的一個幺 半羣而已,這有什麼難以理解的?」
1991- 荷蘭程序員Guido van Rossum爲了一次神祕的手術而進行了一次阿根廷之旅。回來後他帶着一個巨大的顱疤,發明了Python,而被數以軍團計的追隨者們加冕爲「終生大獨裁 者」,並向全世界宣佈「要辦到一件事情,只可有惟一的一種方法!」。整個波蘭陷入了恐慌。
:開源社區一種流行的說法,「仁慈的」終生大獨裁者。這個說法最先指的就是Guido van Rossum。
希特勒在提出創建「純正的雅利安人國家」「統一的大德意志帝國」並實現了德奧合併以後,翌年便入侵了波蘭,引起了第二次世界大戰。「我一我的征服了整個歐洲!」(感受好棒好棒的)
1995- 在家門口附近的一個意大利飯館用餐時,Rasmus Lerdorf意識到他吃的那盤意麪正好是一個用來理解WWW萬維網的極好模型,而全部的Web應用都應該仿照它們的媒介那樣去作。在他的餐巾的背後,他 設計出了著名的「可編程超連接Pasta(Programmable Hyperlinked Pasta,PHP)」語言。PHP的文檔至今仍然保留在那片餐巾上。
PHP最顯著的特色就是:代碼是能夠直接嵌在HTML文檔中的。
1995- 松本「Mad Matz」行弘創造出了Ruby語言,用來闢謠一些意味不明的、有關澳洲將會變成一片由莫霍克族戰士和Tina Turner統治的荒漠的末世預言。該語言後來被它的真正發明者David Heinemeier Hansson從新命名爲Ruby on Rails。(關於某個叫松本行弘的人發明了一種叫作Ruby的語言這件事情從未發生過,最好在這篇文章的下一個版本中刪掉。- DHH表示)
最先關於Ruby的國際會議不是RubyConf,而是每一年在澳大利亞舉辦的OSDConf。
這裏應該是在吐槽Ruby的殺手級應用Ruby on Rails實在太有名了,以致於超越了原來的Ruby語言自己。
Matz並無爲「Ruby」這個名字註冊商標——本着開源的黑客精神。
而DHH(RoR的做者)卻把「Ruby on Rails」這個(包含了「Ruby」字樣的)名稱註冊成了商標,而且阻止別人未經受權使用「Rails」這個名字。
(雖然搶注商標對開源來講未必是一件壞事情——Python基金會今年在歐洲還捲入了一場商標之爭了不是)
假如你歷來沒據說過的話,莫霍克族戰士聽說是像這個樣子的:
而看起來是這個樣子的:
1995- Brendan Eich讀完了歷史上全部在程序語言設計中曾經出現過的錯誤,本身又發明了一些更多的錯誤,而後用它們創造出了Live。以後,爲了緊跟 Java語言的時髦潮流,它被從新命名爲Java。再而後,爲了追隨一種皮膚病的時髦潮流,這語言又被命名爲ECMA。
1996- James Gosling發明了Java。Java是一個相對繁冗的、帶垃圾收集的、基於類的、靜態類型的、單分派的面嚮對象語言,擁有單實現繼承和多接口繼承。Sun竭盡全力地宣傳着Java的獨一無二與衆不同之處。
2001- Anders Hejlsberg發明了C#。C#是一個相對繁冗的、帶垃圾收集的、基於類的、靜態類型的、單分派的面嚮對象語言,擁有單實現繼承和多接口繼承。微軟竭盡全力地宣傳着C#的獨一無二與衆不同之處。
2003- 一個叫Martin Odersky的醉漢看見了好時瑞森花生醬杯的廣告,展現了某我的的花生醬倒入另外一我的的巧克力的場景,他突然有了個點子。他創造了Scala,一種結合 了面向對象和函數式編程的語言。這同時激怒了兩個陣營的忠實信徒,他們馬上宣佈要發動聖戰燒死異教徒。
程序設計離不開編程語言,可是編程語言在國內的大環境中彷佛一直是個二等公民。國內的計算機教育和工程培訓,彷佛一直在宣傳「語言不重要,重要的是思想」,「語言一通百通」等觀點,甚至在許多人眼中「語言的討論」徹底是不入流的,但其實「編程語言」與「工具」、「框架」或是「開發方法」等事物同樣,都對生產力有着重要的影響。事實上,語言的發展歷史比其餘方面更爲悠久,而且在過去十幾年,甚至最近幾年中都依然在不斷的碰撞,演變。期間一些新的語言誕生了,而另外一些在當時看來陽春白雪的語言和編程範式也從新得到了人們的重視。
Anders Hejlsberg是微軟的Technical Fellow,擔任C#編程語言的首席架構師,也參與了.NET Framework,以及VB.NET和F#等語言的設計與開發。幾個月前,Anders在比利時的TechDays 2010及荷蘭DevDays 2010分別進行了一場演講,闡述了他眼中對於編程語言的發展趨勢及將來方向,本文便對他的觀點進行了總結。
大約25到30年前,Anders開發了著名的Turbo Pascal,這是一套集語言、編譯器及開發工具於一體的產品,這也是Anders進入編程語言這一領域的起點。Anders談到,現在的計算機和當年他開發的Turbo Pascal所用的Z-80已經不可同日而語。從那時算起,現在的機器已經有大約10萬倍的外部存儲容量,1萬倍的內存大小,CPU速度也有大約1000倍的提升。可是,若是咱們比較現在的Java代碼及當年Pascal代碼,會發現它們的差異其實並不大。Anders認爲編程語言的發展很是緩慢,期間固然出現了一些東西,例如面向對象等等,可是遠沒有好上1000倍。事實上,近幾十年來的努力主要體如今框架及工具等方面(以下圖)。例如.NET Framework裏有超過一萬個類及十萬個方法,與Turbo Pascal相比的確有了超過1000倍的增加。一樣相似,如今的IDE包含了無數強大的功能,例如語法提示,重構,調試器等等。與此相比,編程語言的改進的確很不明顯。
在過去五、60年的編程歷史中,編程語言的抽象級別不斷提升,人們都在努力讓編程語言更有表現力,這樣咱們能夠用更少的代碼完成更多的工做。咱們一開始使用匯編,而後使用面向過程的語言(如Pascal和C),而後是面嚮對象語言(如C++),隨後便進入了託管時代,語言運行於受託管的執行環境上(如C#,Java),它們的主要特性有自動的垃圾收集,類型安全等等。Anders認爲這樣的趨勢還會繼續保持下去,咱們還會看到抽象級別愈來愈高的語言,而語言的設計者則必須理解並預測下一個抽象級別是什麼樣子的。另外一方面,如.NET,Java等框架的重要性提升了許多,編程語言每每都傾向於構建於現有的工具上,而不會從頭寫起。如今出現的編程語言,例如F#,以及Java領域的Scala,Clojure等等,它們都是基於現有框架構建的,每次從頭開始的代價實在過高。
在Anders眼中,現在影響力較大的趨勢主要有三種(以下圖),它們分別是「聲明式的編程風格」(包括「領域特定語言」及「函數式編程」)、過去的五年很是火熱的「動態語言」(其最重要的方面即是「元編程」能力)以及多核環境下的「併發編程。此外隨着語言的發展,本來經常使用的「面向對象」語言,「動態語言」或是「函數式」等邊界也變得愈來愈模糊,例如各類主要的編程語言都受到函數式語言的影響。所以,「多範式」程序設計語言也是一個愈發明顯的趨勢。
聲明式編程與DSL
目前常見的編程語言大都是命令式(Imperative)的,例如C#,Java或是C++等等。這些語言的特徵在於,代碼裏不只表現了「作什麼(What)」,而更多表現出「如何(How)完成工做」這樣的實現細節,例如for循環,i += 1等等,甚至這部分細節會掩蓋了咱們的「最終目標」。在Anders看來,命令式編程一般會讓代碼變得十分冗餘,更重要的是因爲它提供了過於具體的指令,這樣執行代碼的基礎設施(如CLR或JVM)沒有太多發揮空間,只能老老實實地根據指令一步步的向目標前進。例如,並行執行程序會變得十分困難,由於像「執行目的」這樣更高層次的信息已經丟失了。所以,編程語言的趨勢之一,即是能讓代碼包含更多的「What」,而不是「How」,這樣執行環境即可以更加聰明地去適應當前的執行要求。
關於聲明式的編程風格,Anders主要提出了兩個方面,第一個方面是DSL(Domain Specific Language,領域特定語言)。DSL不是什麼新鮮的玩意兒,咱們平時常常接觸的SQL,CSS,正則表達式等等都屬於DSL。有的DSL可能更加專一於一個方面,例如Mathematica,LOGO等等。這些語言的目標都是特定的領域,與之相對的則是GPPL(General Purpose Programming Language,通用目的編程語言)。Martin Fowler將DSL分爲外部DSL及內部DSL兩種。外部DSL有本身的特定語法、解析器和詞法分析器等等,它們每每是一種小型的編程語言,甚至不會像GPPL那樣須要源文件。與之相對的則是內部DSL。內部DSL其實更像是種別稱,它表明一類特別API及使用模式。
XSLT,SQL等等均可以算做是外部DSL。外部DSL通常會直接針對特定的領域設計,而不考慮其餘方面。James Gosling曾經說過:每一個配置文件最終都會變成一門編程語言。一開始您可能只會用它表示一點點東西,慢慢地您便會想要一些規則,而這些規則則變成了表達式,後來您可能還會定義變量,進行條件判斷等等,而最終它就變成了一種奇怪的編程語言,這樣的狀況家常便飯。如今有一些公司也在關注DSL的開發。例如之前在微軟工做的Charles Simonyi提出了Intentional Programming的概念,還有JetBrains公司提供的一個叫作MPS(Meta Programming System)的產品。最近微軟也提出了本身的Oslo項目,而在Eclipse世界裏也有Xtext,因此其實現在在這方面也有很多人在嘗試。因爲外部DSL的獨立性,在某些狀況下也會出現特定的工具,輔助領域專家或是開發人員自己編寫DSL代碼。還有一些DSL會以XML方言的形式提出,利用XML方言的好處在於有很多現成的工具可用,這樣能夠更快地定義本身的語法。
而內部DSL,正像以前提到的那樣,它每每只是表明了一系列特別的API及使用模式,例如LINQ查詢語句及Ruby on Rails中的Active Record聲明代碼等等。內部DSL能夠使用一系列API來「假裝」成一種DSL,它每每會利用一些「流暢化」的技巧,例如像jQuery那樣把一些方法經過「點」鏈接起來,而另外一些也會利用元編程的方式。內部DSL還有一些優點,例如能夠訪問語言中的代碼或變量,以及利用代碼補全,重構等母語言的全部特性。
DSL的可讀性每每很高。例如,要篩選出單價大於20的產品,並對所屬種類進行分組,並降序地列出每組的分類名稱及產品數量。若是是用命令式的編程方式,則多是這樣的:
顯然這些代碼編寫起來須要一點時間,且很難直接看出它的真實目的,換言之「What」幾乎徹底被「How」所代替了。這樣,一個新的程序員必須花費必定時間才能理解這段代碼的目的。但若是使用LINQ,代碼即可以改寫成:
這段代碼更加關注的是「What」而不是「How」,它不會明確地給出過濾的「操做方式」,也沒有涉及到建立字典這樣的細節。這段代碼還能夠利用C# 3.0中內置的DSL,即LINQ查詢語句來改寫:
編譯器會簡單地將LINQ差距語句轉化爲前一種形式。這段代碼只是表現出最終的目的,而不是明確指定作事的方式,這樣即可以很容易地並行執行這段代碼,如使用PINQ則幾乎不須要作出任何修改。
函數式編程
Anders提出的另外一個重要的聲明式編程方式即是函數式編程。函數式編程歷史悠久,它幾乎和編程語言自己同時誕生,如當年的LISP即是個函數式編程語言。除了LISP之外還有其餘許多函數式編程語言,如APL、Haskell、ML等等。關於函數式編程在學術界已經有過許多研究了,大約在5到10年前許多人開始吸取和整理這些研究內容,想要把它們融入更爲通用的編程語言。如今的編程語言,如C#、Python、Ruby、Scala等等,它們都受到了函數式編程語言的影響。
使用命令式編程語言寫程序時,咱們常常會編寫如x = x + 1這樣的語句,此時咱們大量依賴的是可變狀態,或者說是「變量」,它們的值能夠隨程序運行而改變。可變狀態很是強大,但隨之而來的即是被稱爲「反作用」的問題,例如一個無需參數的void方法,它會根據調用次數或是在哪一個線程上進行調用對程序產生影響,它會改變程序內部的狀態,從而影響以後的運行效果。而在函數式編程中則不會出現這個狀況,由於全部的狀態都是不可變的。事實上對函數式編程的討論更像是數學、公式,而不是程序語句,如x = x + 1對於數學家來講,彷佛只是個永不爲真的表達式而已。
函數式編程十分容易並行,由於它在運行時不會修改任何狀態,所以不管多少線程在運行時均可以觀察到正確的結果。假如兩個函數徹底無關,那麼它們是並行仍是順序地執行便沒有什麼區別了。固然,現實中的程序必定是有反作用的,例如向屏幕輸出內容,向Socket傳輸數據等等,所以真實世界中的函數式編程每每都會考慮如何將有反作用的代碼分離出來。函數式編程默認是不可變的,開發人員必須作些額外的事情才能使用可變狀態或是危險的反作用,與之相反,如C#或Java必須使用readonly或是final來作到這一點。此時,使用函數式編程語言時的思惟觀念便會有所不一樣了。
F#是微軟隨VS 2010推出的一門函數式編程語言,它基於OCaml的核心部分,所以是一門強類型編程語言,並支持一些如模式匹配,類型推斷等現代函數式編程語言的特性。在此之上,F#又增長了異步工做流,度量單位等較爲前沿的語言功能。在F#中若是要計算一個列表全部元素之和,也能夠使用命令式的風格來編寫代碼:
acc只不過,F#中的一切默認都是不可變的,開發人員須要使用mutable關鍵字來聲明一個可變的狀態。事實上,在F#中更典型作法是:
在數學裏咱們常用遞歸,把一個公式分解成幾個變化的形式,以此進行遞歸的定義。純函數式的代碼其「數學性」較強,若是您分析上面這段代碼,會發現它幾乎就是標準的數學定義。在編程時咱們也使用遞歸的作法,編譯器會設法幫咱們轉化成尾調用或是循環語句。
動態語言與元編程
動態語言不會嚴格區分「編譯時」和「運行時」。對於一些靜態編程語言(如C#),每每是先進行編譯,此時可能會獲得一些編譯期錯誤,而對於動態語言來講這兩個階段便混合在一塊兒了。常見的動態語言有JavaScript,Python,Ruby,LISP等等。動態語言和靜態語言各有一些優點,這也是兩個陣營爭論多年的內容。不過Anders認爲它們各自都有十分重要的優勢,而將來不屬於其中任何一方。他表示,從編程語言發展過程當中能夠觀察到兩種特色正在合併的趨勢,將來應該屬於二者的雜交產物。
許多人認定動態語言執行起來很慢,也沒有類型安全等等。例若有這樣一段代碼:
這段代碼在C#和JavaScript中都是合法的,可是它們的處理方式截然不同。在C#中,編譯器能夠推斷出a和n都是32位整數,則for循環和相加操做都只是簡單的CPU指令,天然效率很高。可是對於JavaScript等動態類型語言來講,var只表明了「一個值」,它能夠是任意類型,所以這裏其實還會包含一個「類型標記」,代表它在運行時是什麼類型的對象。因此二者的區別之一即是,表示一樣的值在動態語言中會有一些額外的開銷,在現在的CPU中,「空間」也意味着「速度」,因此較大的值便須要較長時間進行處理,這裏便損失了一部分效率。此外JavaScript在計算a加i時,那麼必須先查看兩個變量中的類型標記,根據類型選擇出合適的相加操做,而後加載兩個值,最後再進行加法操做,一旦越界了還要利用double。很明顯在這裏也會帶來許多開銷。通常來講,動態語言是使用解釋器來執行的,所以還有一些解釋器須要的二進制碼,把這些性能損失所有加起來之後,便會發現執行代碼時須要10倍到100倍的性能開銷。
不過近幾年出現的一些動態虛擬機或引擎將此類狀況改善了許多。現在大部分的JavaScript引擎使用了JIT編譯器,因而便省下了解釋器的開銷,這樣性能損失便會減少至3到10倍。而在過去的兩三年間,JIT編譯器也變得愈來愈高效,瀏覽器中新一代的適應性JIT編譯器,如TraceMonkey,V8,還有微軟在IE 9中使用的Chakra引擎。這種適應性的JIT編譯器使用了一部分有趣的技術,如Inline Caching、Type Specialization、Hidden Classes、Tracing等等,它們能夠將開銷下降至2到3倍的範圍內,這種效率的提高可謂十分神奇。在Anders看來,JavaScript引擎可能已經接近了性能優化的極限,咱們在效率上能夠提高的空間已經很少。不過他一樣認爲,現在JavaScript語言的性能已經足夠快了,徹底有能力做爲Web客戶端的統治性語言。
動態語言的關鍵之一即是「元編程」,「元編程」其實是「代碼生成」的一種別稱,在平常應用中開發人員其實常常依賴這種作法了。在某些場景下使用動態語言會比靜態語言更加天然一些。例如在C#或Java裏使用ORM時,一種傳統作法是讓代碼生成器去觀察數據庫,並生成一大堆代碼,而後再編譯。而動態語言並無編譯期和執行期的區別,例如在Ruby on Rails中使用ActiveRecord便無須定義各式字段。
Anders談到,他和他的團隊也在努力改進靜態語言的元編程能力,如他們正在實現的「編譯器即服務(Compiler as a Service)」。傳統的編譯器是一個黑盒,一端輸入代碼,而另外一端便會生成.NET程序集等數據,開發人員很難參與或理解它的工做。可是在不少時候,開發人員並不必定須要編譯器來生成程序集,他們須要的是一些樹狀的表現形式,而後對它進行識別和重寫。所以,開發人員可能會愈來愈須要一些開放編譯器功能的API。這麼作可讓靜態類型語言得到許多有用的功能,包括元編程以及可操做的完整對象模型等等。
併發
Anders看來,多核革命的一個有趣之處在於,它會要求併發的思惟方式有所改變。傳統的併發思惟,是在單個CPU上執行多個邏輯任務,使用舊有的分時方式或是時間片模型來執行多個任務。可是現在的併發場景則正好相反,是要將一個邏輯上的任務放在多個CPU上執行。這改變了咱們編寫程序的方式,這意味着對於語言或是API來講,咱們須要有辦法來分解任務,把它拆分紅多個小任務後獨立的執行,而傳統的編程語言中並不關注這點。
使用目前的併發API來完成工做並不容易,好比Thread,ThreadPool,Monitor等等,開發人員很難走的太遠。不過在.NET 4.0中提供了一套強大的框架,即.NET並行擴展(Parallel Extensions),這是一種現代的併發模型,將邏輯上的任務併發與實際使用的的物理模型分離開來。之前的API都是直接處理線程等基礎元素,不過利用.NET並行擴展中的任務並行庫(Task Parallel Library),並行LINQ(Parallel LINQ)以及協調數據結構(Coordination Data Structures)讓開發人員能夠直接關注邏輯上的任務,而沒必要關心它們是如何運行的,或是使用了多少個線程和CPU等等。利用LINQ這樣的DSL也有助於寫出並行的代碼,若是使用普通的for循環配合線程池來實現並行,則開發人員很容易在各類API裏失去方向。
不過事實上,編寫並行的代碼依然很困難,尤爲是要識別出能夠並行的地方。Anders認爲不少時候仍是須要編程語言來關注這方面的事情(以下圖)。好比「隔離性(Isolation)」,即編譯器如何發現這段代碼是獨立的,即可以將其安全地併發執行。某段代碼建立了一個對象,在分享給其餘人以前,咱們對它的改變是安全的,可是一旦將其共享出去之後便徹底不一樣了。所以理想中的類型系統應該能夠跟蹤到這樣的共享,如Linear Types——這在學術界也有一些研究。編程語言也能夠在函數的純潔性(Purity)方面下功夫,如關注某個函數是否有反作用,有些時候編譯器能夠作這方面的檢查,它能夠禁止某些操做,以此保證咱們寫出無反作用的純函數。另外即是不可變性(Immutability),目前的語言,如C#或VB,咱們須要額外的工做才能寫出不可變的代碼。Anders認爲合適的作法應該是在語言層面上更好的支持不可變性。這些都是在併發方面須要考慮的問題。
Anders還提到了他在思考併發語言特性時所遵循的原則:一個語言特性不該該針對某個特定的併發模型,而應該是一種通用的,可用於各類不一樣的併發場景的特性,就像隔離性、純潔性及不可變性那樣。語言擁有這樣的特性以後,就能夠用於構建各類不一樣的API,各類併發方式均可以利用到核心的語言特性。
參考:
https://www.sohu.com/a/136310616_505803
https://blog.csdn.net/u010178308/article/details/79001888