胡談程式語言:從C語言到Julia
犀見日月,天地方圓
有一天你會遇見一個彩虹般絢爛的人,從此以後,其他人不過匆匆浮雲。
C語言
我想起人在童年矇昧之時第一次在意識深處注意到並思考著太陽月亮時候的心情:它們是什麼?
“太陽和月亮”,大人們有力的回答。
我想如果我活在一千年前,在我終於學會了太陽和月亮的概念之後,不用多久我就會知道另外四個字:天圓地方。
人之初見,幾乎不能做什麼有力的思考,只能被動的先接受一些東西,然後才能反過來懷疑這些基礎是否完全合理。
C語言是偉大,幾乎現代的IT業都構建於C語言之上—— OS,網路基礎設施,都是如此。C如此重要,也許應該說C語言本身承擔了某種重要的角色。當人類終於從機器語言變換到組合語言之後,彙編仍然直接操控機器,最接近於本質才最強大,才能掌控一切。彙編是苦的,用起來一點兒也不甜,糖是必須的。所以將C語言看作是一門高階的組合語言,繼承了彙編對硬體的掌控能力,用起來還不苦了,似乎更符合其角色。畢竟C是天地初開那一劃,給人世帶來最新的認知:犀見日月,天地方圓。變數定義、型別系統、if、while、switch、return等語法結構如同陰陽五行一般成為構建、理解整個世界的基礎。
OOP之C++ & Objective-c
兩種最為有名的C語言在OOP思想下的變體。C既然承接於彙編,好的部分當然要繼承,彙編不好的部分你敢丟棄麼?C++承接C而繼續向前發展,與C遇到了相同的問題:一部分的東西可以用OOP包裝的更好更強大,但是那些不好處理的部分怎麼辦?一種方式是強行將所有東西統合在新框架中,激進派,結構完美主義;另一種方案是,one for the best。如果OOP的效力有限,那就讓OOP把自己能做的事情最優雅的做到最好,其他的留給C語言本身就好了。是的,C++採取了前一種方案,將所有C語言所能幹到的事情,對於程式的掌控能力都強行統合在一個框架中,所以你看到超級的複雜性,一些語法在區域性的醜陋不堪。Objective-C所選擇的道路我甚為贊善,明智的分工合作而不是想著自己全包乾。
Java
Java就像個幹什麼事情都需要人服侍,打扮得冠冕堂皇的貴婦一般,容不得一丁點的不合禮法。誠然Java強大,橫掃天下,能在諸多領域展現自己姿勢和實力,但她的確太雍容了,以至於每個地方都要忍受她的至高無上的行事風格。
還記得吧,那些年我們是這樣寫Java的hello world的:
public static void main(string[] args)
{
System.out.println("Hello World");
}
哦,別忘了,咱們得用類包裝一下,所以得像這樣:
Class Main
{
public static void main (string[] args)
{
System.out.println("Hello World");
}
}
貴族總有那麼多奢侈的禮法,你要是願意時時刻刻都這樣伺候Java,嘿,小夥,祝你前途坦蕩,平步青雲,DOG RICH,NO FORGET.
JavaScript
毋庸置疑JS已近統治了前端,V8和Node.js的發力將JS的影響力以一種開天闢地的方式帶入到後端世界。CoffeeScript等JavaScript的包裝語言也不斷站在巨人的肩膀上,攻城略地。更重要的是仔細看看chrome的威力吧。在C/S模式中,web化大勢所趨。瀏覽器將成為除開OS之外最重要的軟體。服務將基於HTML5等新標準全面web化。so,這跟JS有幾毛錢關係。HTML5 + CSS3進行頁面表現的控制,那麼當要使用瀏覽器在本地對事件進行一些處理的時候怎麼呼叫瀏覽器?和現在一樣,瀏覽器支援某種前端語言,提供API,如同OS提供系統服務一般將broswer自身的服務開放出去。JS是首選,不僅僅是它現在統治了前端,更是因為對於JS優化的努力已近很明顯的促成了JS成為一箇中間平臺,可以讓CoffeeScript等語言一面兼顧對程式設計師的友好性,另一方面不必費盡心思,從頭開始進行優化,更重要的是JS原來那些庫、框架、有用的Code仍然可以無縫整合!是了,JS越來越成為一箇中間平臺,一個連線web底層服務和上層應用程式碼的虛擬機器。用C我們搞出unix、linux這樣的平臺來執行各種語言編寫的程式,JS也能在web上架構起V8這樣的系統級開發平臺。OS於V8的共同之處在於對於更上層的application來說,它們都只是容器而已。
Opa
Opa是什麼?
問問自己如果從前端到後端要掌握多少東西吧!web開發始終有一個問題是前端和後端的開發語言不是一樣的。這真是要了程式設計師的命,特別是國內的程式設計師,可知老闆有多希望你是一個全棧式的Coder啊。前後端一統的開發模式是一種難以避免的需求。是JS,還是JS。目前也只有JS同時在前端和後端都混得風生水起。但道路既然已近指明,競爭還不會出現麼?感謝上帝。It’s Opa.
Opa對比JS來說,Opa有強大得後發優勢,並且是經過專業團隊,專項研究,專門針對WEB程式設計而設計的傑作。後發優勢使得Opa一方面吸取Web開發各家之長,並將一些隨著演進才出現的問題(這些問題在JS/PHP等語言誕生都還不明顯,或者設計時沒有足夠好的解決方案,已近讓JS/PHP開發人員受盡折磨)內建在語言中給出優雅的解決方案。或對於同樣的問題給出前代語言更為高效、更為現代化、更具程式設計師友好性的解決方案。使用Opa語法比JS更簡潔,不讓CoffeeScript;從此前後端一統,更重要的是Opa生在雲時代,對於雲的銜接是JS、PHP這些前代語言完全無法匹敵的一個所在。
每一個現代的WEB開發人員,都將不可避免的接觸兩種資料格式:JSON和XML。JSON是JS內嵌的資料結構,可以直接轉化為Obj進行操作。XML呢?這麼多年了,有什麼語言把對XML的支援內嵌到語言中去了。我已近寫煩了C/C++的XML讀寫和解析了。大量重複的程式碼,大量無聊的工作僅僅實現了一個XML檔案到一個記憶體物件的轉換。阿門,無知是罪,浪費更是罪。Opa是專為Web程式設計、並且專注於Web程式設計的語言,內建XML支援,可以直接將XML定義為Opa的工作物件。這意味著我們可以保持XML檔案良好的閱讀性的同時,還能夠直接將一個XML檔案反序列化成為一個Opa的XML物件,終於擺脫了該死的XML解析之罪。
Haskell
神人造出的東西總是美得讓人難以置信。
尤拉恆等式
Einstain 質能方程
所以程式設計界呢?
It’s Haskell.
一大群PhD搞出的東西,能怎樣形容一下呢,優雅,優雅,還是優雅!Haskell基於數學,掌握它之後你可以把你的程式碼寫的像英文一樣簡單易懂。易懂的不僅僅是別人閱讀你的程式碼時易懂,而是程式設計師本身就可以按照數學的邏輯和語言表達的自然順序快速的寫出程式來。你可以想象自己是在做數學證明題,而最後驚奇的發現你已近寫好了解決實際問題的程式碼。惰性計算和函式不變態將使你免於指令式程式設計的諸多災難。
那麼問題是為什麼Haskell不流行?
借用流行的說法是:有些語言流行僅僅是因為它們流行而已。意即它們之所以流行是因為它們是流行的。這是一個遞迴。
所以現實的情況是,haskell的確優雅美麗,強大有效,但就是不流行。故而像要使用Haskell混飯吃是一件十分困難的事。至少國內困難的基本無法找到有規模性質的招聘資訊。但學習Haskell是值得的,就像學習數學和物理是值得的道理是一樣的。
終於擺脫C系語言無聊的括號,我欣然接受函式組合式牛仔新技能。
我們真的需要那麼複雜的語言麼?Null是個鬼!
OOP不是隻有OOP才是一種實現。Haskell將帶你領略出於數學方法的精妙解答。
Pyhton
指令碼、動態、函式式。Python是令人著迷的。哲學比任何其他東西都更能說明一切。C++/Java很有科學的道理,但是哲學呢?
Python是一門以哲學為指導的語言。有好的哲學才有好的文化,有好的文化才有好的社群。對於開發者來說,選擇一個語言就像選擇一個國家的國籍,你不僅僅購買語法和語義,你也購買了經濟和文化,以及你怎樣獲得生計和力量的規則,就像他們常說的:為了避免一個“死”語言控制了世界,購買需要謹慎。【引用網文,作者不詳】相比Haskell來說,Python提供了一個優雅而現實的解決方案,甚至在google中也能並肩成為其第二大開發語言(至少在前幾年是這樣的)。雖然Python的執行速度較慢,但這和Objective-c殊途同歸,自己搞不定的交給別人,信任別人就好了。效能麼?哥們呼叫C就OK了。
Julia:Walk as Python,Run as C
Julia的目標在於讓高效能的科學計算人人可用。是的,這看起來並不是為通用程式設計領域設計的語言。But,事實也是這樣嗎?
Walk as Python,不僅僅形態和語法與Pyhton相似,而且比Python更優雅,另一個好訊息是Julia和Python是可以無縫對接的。你可以在任何你想的地方使用Python(當然如果Julia更好的時候我不建議你這樣做,我相信Python也不建議這麼做,因為這有悖於Python的哲學:There should be one– and preferably only one –obvious way to do it.),不過那麼多Python庫我們怎麼能放過呢?盡情拿過來使用吧。
Run as C 美麗的Julia克服了Python的致命弱點!美貌才是這個世界上最難打理的奢侈品。美麗不容易,但內心的涵養更是難得。有一天你會遇見一個彩虹般絢爛的人,從此以後,其他人不過匆匆浮雲。
終於開到有一門語言的下標不是從0開始的了!感謝上帝。讓我們迴歸真實的世界,自然邏輯的法則啊,不該為了強行將0加入其框架而折磨世人。
Julia是我們同時代的美人,她還很年輕,誕生於2012年。
Go
Go語言從Haskell和Python那裡學到了不少,但還不夠好。文藝要傳達眾人必先得其美。咱們看看Go的變數宣告如何?
var v1 int = 10 // 正確的使用方式1
var v2 = 10 // 正確的使用方式2,編譯器可以自動推匯出v2的型別
v3 := 10 // 正確的使用方式3,編譯器可以自動推匯出v3的型別
所以,當我們需要變數的型別的時候我們就只能在其後面突兀的貼入一個int/double/string的標籤?
func readValues(infile string)(values []int, err error)
這放在函式返回值的時候尤為難看,是真的一種難看。Programming發展的方向是不斷靠近自然語言,但自然語言有太多的資訊冗餘,完全的自然並不適合作為程式語言的最終形態。一種很明顯的區別用來說的語言和用來寫的語言是不盡相同的,它們之間具有其自身良好的區分度,以備在需要的時候能將兩者甄別。語言在有些時候是不必那麼完整的,定義一個int型變數 varx,我們可以這樣說 varx is value of int,變數名在前,變數型別到了最後(Go的做法);另一種同樣達意的說法是 we define a int value varx.我更傾向於我們使用varx的時候我們需要在心裡默唸 varx is value of int,但在第一次正式而完整的定義varx的時候,你難道不是這樣告訴你自己的:定義一個int型變數varx(define a int varx),後面需要用到它。所以我以為這不是文藝的復興,而是某種倒退了。也許10年20年之後我將承認我今天的無知。然而我的辯解是:一個人一個事物表現其特立獨行太過超前拔世,就總會受到某種警告。一旦有些特性超越了世浪太多,我不得不在當下給它貼上一個反人類設計的標籤。
C#
我讀書少,不知道有沒有合適的寓言或者童話故事來貼合一下C#。C#強大和實用性已近經過了驗證。問題是對於C++這樣複雜語言人類都能以之構造精美絕倫的程式,這給我的想法是不論對於多複雜、多麼龐大、多麼難以掌控的工具,人類只要投入進去就能夠取得自己想要的成就,而不是反過來證明了C++是多麼不可或缺。C#的特性太多太多了,每一個版本的更新你幾乎都能看見更新。當然更新是必須,更不能成為非難的藉口。判別的標準在於:頻繁的更新是否將其核心打造的越來越完美無缺,是否在完善其核心功能基礎之上添加了正真有效、有趣的東西;而不是在合適和妝著之上添上了不予自己搭配的其他東西。一套價值10W的衣服配上一個幾十塊地攤貨的包包是怎樣的一種感受?
而另一個問題是我們真的需要如此複雜的工具來解決問題嗎?這一點Go是有哲學的,這是Go帶來的最大的進步:化繁為簡,少即是多。有什麼事情可以用C++幹到而C幹不到?當然得承認有些地方C太低階了,實現起來超級麻煩。不過如果只是解決問題,你需要使用C++這種核武器級別的複雜工具麼?低速狀態下使用牛頓力學就好,不要動不動就拿著相對論的武器進行轟炸。避免大量無謂的計算,世界才能和平。
整體來看,程式語言正在不斷從主流的C系語言已有的桎梏中解放出來,不斷學習動態語言、函數語言程式設計和其他小眾程式設計界湧現出來的優秀特性。從語言發展圖譜來看,語言的確在向著動態和函式式兩個方向遷移。貌似新的語言不說自己支援點函數語言程式設計特性都不好意思是自己新出生的語言了。據說最近微軟的F#語言莫名的火了起來,從F#的定位來看,C#的介面上的優勢F#將在.NET平臺同享,而F#最大的優勢是它比C#樸素了很多,不再新增很多花裡胡哨的東西,更是大規模支援了函數語言程式設計,微軟的改變頗為耐人尋味。
程式語言影響力圖譜
可以看到,在整個程式語言影響力圖譜上,最大最明顯的兩個節點其中一個是C,另一個是LISP/Haskell.
關於C語言不足之處的一些思考
C的不足之處不僅僅在於使用指標時型別和名字不分,更多的時候是關鍵字上設計的失誤,比如static關鍵字在文字全域性使用時表示變數或函式的可見性,在函式中卻用來實現區域性變數的靜態儲存,這嚴重違反了unix“do one thing at a time and do well”的設計哲學,還有空指標這種東西,沒有最好,如果有也不應該使用NULL這種常量式的定義,容易和0本身混淆,void指標任意轉換的設計更是糟透了,這再一次違反了unix的設計哲學,另外不得不說的是常量定義即可以用#define也可以用const,卻還是推薦使用#define來進行定義,然而個人覺得這的確是語言設計上的某種失誤,即最開始時沒有好好考慮誰來承擔定義常量的責任,縱觀C語言整個語言特徵體系,的確是嚴重違反了單一職責的設計原則,並且在那個缺乏語言設計實踐經驗的設計結果就是關鍵字責任的混亂不堪,語義限定的要麼不完整要麼有些臃腫,以現在的視角來看,C語言真是一門缺乏精心設計的語言,很多缺陷都可以明顯的避免。諸如訪問許可權,模組化等功能使用namespace、private、public、protected等關鍵字完全可以實現static、external等關鍵字所想要實現的語言特性,另外一點可能是最致命的一點是C的語言邏輯也可能存在走了彎路的設計,這導致了模組化成本的提升。
在宣告指標時常量指標和指向常量的指標的區別簡直噁心,而這完全可以避免:
ptr<const int> p; //指向常量的指標,指標值可變,指標所指向的變數本身不可被改變
const ptr<int> p; //常量指標,指標值本身不可變,指標所指向的變數本身可以被改變
又如C沒有new這樣的關鍵字操作符來明確區分堆記憶體分配和棧記憶體分配,以至於函式內部宣告的指標也不能返回,嚴重加重了程式設計師的記憶體管理負擔。然而,我們的確需要一個new/delete操作符來明確承擔的堆記憶體管理職責,而不是與靜態儲存區混用。
…….
總而言之,C語言是一門可以設計得更加優美簡潔的語言。
那麼,如果C語言本身可以在不減少其功能的前提下重構為一門更加精巧,簡潔、已用的語言的話,我們就要問問實現系統級的程式設計我們到底需要多少的語言特性呢?
結合使用GCC的擴充套件來實現Linux的事實,我們幾乎可以肯定實現系統級的程式設計以C語言核心特性來說已近足夠,而C語言本身還有精簡和優化的空間。所以實現系統級程式設計並不在本質上要求語言本身被加入了許多的特性,滿足開發需求的核心特性足以讓一門語言十分精簡而保持強大的開發功能,如scheme,如Lua。C語言的複雜度還可以降低而同時保持其已有的開發能力。
設計一門語言,以最為精巧、簡約的語言架構滿足其強大的開發需求,去掉C一些缺陷性的特性,加入一些更加友好的現代特性,但是
第一:語言的特性應該儘量的少;
第二:語言的特性應該職責明確,呈現正交分佈形態;
第三:不要強求多正規化,從C的經驗來看,僅僅是過程式開發就已近具有強大得開發潛力了,雖然有時候缺乏某個特性的開發會顯得十分繁瑣和複雜,但在保持語言本身精巧、簡潔、易用的設計目標下是有一些必要的犧牲,這個方面需要做出明智的權衡;
第四:語言本身的特性和配套的基本庫要具有正交性,這樣才能讓兩者相互配合,發揮出最大的威力;
第五:借鑑、吸收可以,但請丟掉繼承C/C++遺產的想法,丟掉歷史的包袱毫無羈絆的進行自己的設計才是C最偉大之處。
一些思考與筆記
C++就程式語言設計方面來說即不優雅簡潔,又不安全健康,除了傲視群雄的效能似乎也沒有什麼特別值得稱道的實際之處,總體看來還是一個失敗的語言設計。
C/C++是效能工具而不是開發效率工具,而Java是一種很好的效率工具,雖然還不那麼很好。
選用C/C++進行開發的程式碼如果肯下功夫依然可以健壯的執行,但就像你現在還活在這個世界上一樣,不能與時俱進的你,還能對這個世界有幾分影響。
一些在其他語言中不存在的問題卻成為了C++程式設計師必須小心應對的缺陷,需要學習各種具體操作條款、程式設計約定和專門知識以避免陷進,提升開發效率,這簡直無聊透頂,並且C++居然沒有統一的實現,語言自身也沒有版本號(標準文件的版本號和現實還有著實現程度的鴻溝),完全不像JAVA、Python等現代語言一樣有專門的組織機構釋出最新的語言版本和標準庫,而C++的標準委員會只是製作要收費的標準文件而已,但他們並不自己完成開發!!並且C++的標準庫實際上並不那麼簡單易用,強制讓所有資料結構靠向迭代器演算法的結果就是在使用時總是有些特殊處理和注意事項,這樣搞來搞去真的有意思麼?標準庫的失敗對開發人員來說就是一個致命傷。另外C++語言的整體結構都顯得有些混亂不堪。哪些事情應該交給編譯器做,哪些事情需要程式設計師自己操心完全沒能進行良好的區分。本來應該由編譯器隱藏的東西卻偏偏只做一半,還剩一部分又交回給程式設計師是什麼意思?到頭來程式設計師不僅需要了解那些應該被編譯器隱藏的東西,反而又需要深入瞭解編譯器內部的實現邏輯了!難道人就是這麼喜歡給自己找事兒麼?總之,C++畢竟是中古時代遺留下來的語言,其整體設計和運作方式完全保持了自己老舊的學院式做派,在現代程式語言界簡直是個個性十足的奇葩。
編譯器與程式設計師 Compiler & Programmer
程式設計師不瞭解他的編譯器簡直就是災難!在整個程式開發過程中程式設計師和編譯器之間的關係十分微妙,應該得到十二分的重視,然而我們的教育卻並未這樣做,這真是無可奈何的悲哀!
在程式開發過程中程式設計師和編譯器各承擔什麼責任,誰做了什麼事情,誰在什麼時候會做什麼事情,誰在什麼時候該做什麼事情都必須明確。這需要程式設計師十分了解自己所用的編譯器,不然程式設計師所謂的開發實際上就是盲寫,是一種誤打誤撞,這種態度應該被專業程式設計師所拋棄!我們需要一門怎樣的程式語言?
這門語言要有C一樣簡單與效能,具備C++和Java強大的抽象和建模能力,還要像Haskell和Python一樣優雅整潔,支援多種程式設計正規化,能同時滿足系統級、平臺級和應用級程式設計需要,能適應大型工業級軟體開發的嚴苛挑戰,具有通用性,還要擁有足夠多的優秀且豐富的庫和框架,以及一整條完整好用的配套開發工具鏈。通用程式設計語言如C系語言採用了自底向上的進化模式,不斷從機器層面抽象出更自然的語言系統,而LISP系語言似乎是以自頂向下的延展方式發展著,以不斷從數學似的語言中泛化為更加自然的語言系統。C和LISP一者面向機器底層,一直面向通用圖靈機模型,是一種兩極化的存在關係,但總體趨勢都是在向著自然語言系統方向發展,兩者必將有所交匯而產生一種強大而至美的通用程式語言,那麼會是什麼呢?是Haskell麼?是Julia麼?
最後的選擇
在系統級和大型高效能程式的開發上我們已近有了C和C++,但在開發語言的進化圖譜上我們需要更進一步了。這已近迫在眉睫。C++為了相容C而揹負和犧牲了太多太多,而Java因先天對多程式設計正規化支援不足已顯後勁不足。我們不能要求未來的年輕人也必須揹著這些包袱才能繼續在軟體界前行。這些已近狠狠折磨一代人了。新的時代需要新的語言,是時候做出選擇和改變了。如果你活在這個時代,那請關注Clojure、Rust、Go、Python、Haskell、Julia吧。如果你生活在下一個程式設計時代,不要懷疑,請把這些語言換成你們時代新出現的語言,未來應該掌握在你們自己手中。
Life is short, u must find what u love.