一位國(guó)外老程序員的反思:C、Python、Java 不可兼得,專心學(xué)好一門編程語(yǔ)言就行!
最近,著名游戲程序員、id Software 創(chuàng)始人之一John Carmack在采訪中表示,程序員應(yīng)該專心學(xué)好一門編程語(yǔ)言。這倒讓我感到有點(diǎn)驚訝。雖然我個(gè)人非常贊同這條建議,但在如今的程序員圈子里,這種觀點(diǎn)是有爭(zhēng)議的。
我猜,我就是大家所說(shuō)的“老程序員”了。我的歲數(shù)不小了,一生都在從事編程工作,而且從步入社會(huì)之后就一直在從事這項(xiàng)專業(yè)工作。有時(shí),我覺(jué)得自己是一名編程語(yǔ)言愛好者,親眼目睹了許多編程語(yǔ)言的發(fā)展?;仡欉^(guò)去,這是一段激動(dòng)人心的歷史,我們會(huì)不由自主地得出一個(gè)(錯(cuò)誤的)結(jié)論:多掌握幾種編程語(yǔ)言總沒(méi)壞處。編程語(yǔ)言的歷史發(fā)展非常精彩,但如今的發(fā)展形勢(shì)相對(duì)比較平和。
在本文中我想回顧一下曾經(jīng)的歷史,總結(jié)經(jīng)驗(yàn)教訓(xùn),并看一看究竟哪種編程語(yǔ)言才是最好的標(biāo)準(zhǔn)化語(yǔ)言。
史前階段(50年代~80年代)
隨著計(jì)算硬件和計(jì)算機(jī)科學(xué)作為一門學(xué)科逐漸興起,計(jì)算機(jī)編程(除了處理器本身的指令之外)也開始緩慢地發(fā)展。在最初的幾十年里,編程語(yǔ)言主要是學(xué)術(shù)界的研究對(duì)象,還俘獲了一小部分研究人員。程序員的選擇很有限,主要取決于領(lǐng)域。
業(yè)務(wù)編程使用COBOL,科學(xué)編程使用Fortran,還有一些其他語(yǔ)言通常用于特定領(lǐng)域、研究或硬件。
對(duì)于大多數(shù)程序員來(lái)說(shuō),整個(gè)編程生涯或在很長(zhǎng)一段時(shí)間里,只需專心學(xué)習(xí)一門編程語(yǔ)言。雖然有人對(duì)編程語(yǔ)言的設(shè)計(jì)感興趣,但彼時(shí)該領(lǐng)域還很稚嫩。盡管出現(xiàn)了一些很有趣的創(chuàng)新,但對(duì)于如何才能設(shè)計(jì)一種好的編程語(yǔ)言,人們還沒(méi)有很好的理解。
專業(yè)化(80年代~90年代)
隨著計(jì)算機(jī)硬件數(shù)量的增加以及用途的日益多樣化,編程語(yǔ)言的數(shù)量也開始增長(zhǎng),編程語(yǔ)言的選擇變成了一個(gè)流行的話題。人們開始對(duì)編程語(yǔ)言進(jìn)行分門別類。我們可以通過(guò)程序員的種類以及他們渴望達(dá)到的專業(yè)水平,判斷他們會(huì)選擇哪種語(yǔ)言。
個(gè)人計(jì)算機(jī)編程愛好者使用越來(lái)越流行的BASIC。這是一種很荒誕、很原始的編程語(yǔ)言,卻被廣泛使用并成為了一代程序員(包括我自己)的引路人。Pascal引入了結(jié)構(gòu)化編程,并產(chǎn)生了巨大的影響(Pascal與Turbo-Pascal 和 Delphi 建立了一個(gè)蓬勃發(fā)展的社區(qū),但最終消失了)。
起源于UNIX的C成為了系統(tǒng)編程語(yǔ)言。C++成為了C的后繼者,并借鑒了Smalltalk的面向?qū)ο缶幊?,成為了專業(yè)應(yīng)用程序和服務(wù)開發(fā)人員的語(yǔ)言。最終 Visual Basic(與BASIC毫無(wú)關(guān)系)普及了“可視化編程”,滿足了應(yīng)用程序開發(fā)的需求(隨著 Windows 的出現(xiàn)而迅速增長(zhǎng)),并成為大眾的首選。但人們普遍認(rèn)為,VB程序員是領(lǐng)域?qū)<壹媛毦幊坦ぷ?,而C和C++才是“專業(yè)”的編程人員。
這個(gè)階段,人們?nèi)匀粵](méi)有很好地理解編程語(yǔ)言的設(shè)計(jì),導(dǎo)致許多流行的編程語(yǔ)言很多方面的設(shè)計(jì)都不太理想。C語(yǔ)言簡(jiǎn)單而強(qiáng)大,但很難熟練掌握,有可能出錯(cuò)的地方太多。C++的意圖雖好,但最終的設(shè)計(jì)不佳,而且使用感不好。Visual Basic既有趣又簡(jiǎn)單,但有點(diǎn)兒戲,在當(dāng)時(shí)的技術(shù)條件下,優(yōu)雅與效率都不達(dá)標(biāo)。Smalltalk 和 LISP 都是有趣而優(yōu)雅的語(yǔ)言,但由于捆綁到了專門的硬件和昂貴的工具,導(dǎo)致最后失勢(shì)。
成熟(90年代~2000年)
后來(lái),互聯(lián)網(wǎng)興起?;ヂ?lián)網(wǎng)對(duì)編程語(yǔ)言的影響究竟有多大也許未可知,但無(wú)疑這是一個(gè)重大因素。很久以前,編程語(yǔ)言是一個(gè)稀有之物,通常誕生于研究實(shí)驗(yàn)室和大型商業(yè)公司;但如今似乎任何一個(gè)人都可以開發(fā)出自己的編程語(yǔ)言。曾有一段時(shí)間,PERL成為了流行的通用語(yǔ)言,涵蓋了從系統(tǒng)管理到 Web 編程的方方面面。
后來(lái),Python從科學(xué)研究語(yǔ)言變成了簡(jiǎn)單易學(xué)的通用語(yǔ)言,盡管最初發(fā)展緩慢,但最終席卷了整個(gè)世界。據(jù)傳,Netscape 的 Brandan Eich僅用了幾天時(shí)間就開發(fā)出了JavaScript(作為一種功能十分有限的瀏覽器擴(kuò)展語(yǔ)言)。這不僅證明Eich是一個(gè)天才,也證明那個(gè)時(shí)期人們對(duì)編程語(yǔ)言的設(shè)計(jì)有了很好的理解。
這一時(shí)期出現(xiàn)了許多其他的編程語(yǔ)言,其中最有名的是Java。這門語(yǔ)言本身并沒(méi)有特別之處,但它提供的JVM是一個(gè)通用的運(yùn)行時(shí)環(huán)境,實(shí)現(xiàn)了“編寫一次,到處運(yùn)行”,也就是說(shuō)該語(yǔ)言十分通用,不受特定硬件、操作系統(tǒng)、或目標(biāo)環(huán)境的限制。嚴(yán)格來(lái)講,早期的JVM并沒(méi)有什么值得炫耀的,但它開創(chuàng)了語(yǔ)言運(yùn)行時(shí)及部署選項(xiàng)日益成熟的時(shí)代。
迅速發(fā)展(2000年~2010年)
自JVM以后,編程語(yǔ)言就開始朝著一個(gè)有趣的方向迅速發(fā)展。源自Self語(yǔ)言(Smalltalk的后繼者,雖然優(yōu)秀但非常失敗)的即時(shí)編譯器(JIT)得到了更深入的研究,從而誕生了Java的HotSpot,而微軟為了對(duì)抗Java推出了.NET CLR。.NET則更進(jìn)一步,將 CLR(Common Language Runtime,公共語(yǔ)言運(yùn)行時(shí))作為了多語(yǔ)言的通用運(yùn)行時(shí),而不僅僅是C#。事后看來(lái),這是一個(gè)分水嶺:編程語(yǔ)言的選擇變得無(wú)關(guān)緊要。
這可能不是微軟做出這個(gè)選擇的主要原因(當(dāng)時(shí)他們?nèi)栽谂^續(xù)支持流行度非常高的Visual Basic,還有C#),再加上那段時(shí)間微軟的封閉式許可,最終CLR未能成為最受歡迎的運(yùn)行環(huán)境。但在千禧年之后的第一個(gè)十年中,編程語(yǔ)言的數(shù)量越來(lái)越多,而且無(wú)處不在。
另一方面,程序員的數(shù)量也出現(xiàn)了爆炸式增長(zhǎng)。隨著軟件的需求快速增長(zhǎng),以及工具和知識(shí)的普及,全世界數(shù)百萬(wàn)人都變成了程序員。這些程序員也是人類,他們渴望強(qiáng)烈的群體認(rèn)同。就像普通人對(duì)體育運(yùn)動(dòng)團(tuán)體有著強(qiáng)烈而非理性的看法一樣,程序員也開始在編程語(yǔ)言的選擇問(wèn)題上站隊(duì)。許多程序員迫不得已選擇某種新興、獨(dú)特、特殊的編程語(yǔ)言。
例如,有人聲稱函數(shù)式編程才是王道、Ruby比Python好、Scala將徹底改變數(shù)據(jù)科學(xué)、不選Clojure是你的損失……至此,編程語(yǔ)言從線性發(fā)展進(jìn)入了混亂的達(dá)爾文優(yōu)勝劣汰時(shí)期。
超標(biāo)準(zhǔn)化(2010年至今)
原以為,這個(gè)時(shí)期的人們會(huì)意識(shí)到某些編程語(yǔ)言過(guò)于瘋狂,無(wú)法持續(xù)發(fā)展。然而,實(shí)際情況并非如此,相反,情況出現(xiàn)了意想不到的轉(zhuǎn)變。在“云”計(jì)算時(shí)代,許多應(yīng)用程序和服務(wù)的部署跨互聯(lián)網(wǎng)上的大量分布式節(jié)點(diǎn),使用哪種編程語(yǔ)言似乎已無(wú)關(guān)緊要。程序員都在開發(fā)互相交流的獨(dú)立組件,又有什么必要糾結(jié)編程語(yǔ)言呢?組件之間并不需要知道彼此是用哪種語(yǔ)言編寫的。如果程序員喜歡用X語(yǔ)言編寫組件,那么就用這種語(yǔ)言好了。誰(shuí)在乎呀。
在不同機(jī)器上運(yùn)行的組件也是如此,隨著Docker的發(fā)布,容器得到了普及,無(wú)論是在一臺(tái)機(jī)器上運(yùn)行的應(yīng)用程序,還是通過(guò)編排軟件在機(jī)器集群上協(xié)作運(yùn)行的軟件,都可以使用相同的范例輕松管理。
如今人們?nèi)栽陂_發(fā)新的編程語(yǔ)言,其中不乏前途無(wú)量且備受期待的語(yǔ)言。有些是特定領(lǐng)域的(移動(dòng)應(yīng)用程序使用的Swift、Kotlin 和 Dart,以太坊智能合約使用的Solidity),而有些則比較通用,但每種語(yǔ)言都得益于這幾十年來(lái)積累的經(jīng)驗(yàn)教訓(xùn)(面向云編程的Go,面向系統(tǒng)編程的Rust,以及JavaScript的超集TypeScript,等等)。
與此同時(shí),編程世界達(dá)到了一個(gè)新的成熟度,我們不再追逐每一種新趨勢(shì),采用每一種新語(yǔ)言。我們都成長(zhǎng)了。
專心學(xué)好一門編程語(yǔ)言
毫無(wú)疑問(wèn),有些編程語(yǔ)言確實(shí)更為出色,而有些編程語(yǔ)言則更適合處理某些特定的用例。任何從事過(guò)編程一段時(shí)間的人都清楚,學(xué)習(xí)一門新語(yǔ)言一點(diǎn)也不難。大多數(shù)程序員只需一個(gè)下午,就可以輕松學(xué)習(xí)一門新語(yǔ)言的基礎(chǔ)知識(shí),使用幾天后就可以多或少地提高工作效率。新手程序員可以從任何一門主流編程語(yǔ)言開始學(xué)習(xí),并將學(xué)到的編程知識(shí)輕松地應(yīng)用到其他語(yǔ)言中。
然而,頻繁變更編程語(yǔ)言并非好事,原因主要有兩個(gè)。首先,學(xué)習(xí)編程語(yǔ)言有點(diǎn)像學(xué)下棋。你可以快速學(xué)習(xí)規(guī)則,但這并不意味著你可以戰(zhàn)勝經(jīng)驗(yàn)豐富的玩家。你需要學(xué)習(xí)策略,而這需要時(shí)間和練習(xí)。這是一個(gè)由最佳實(shí)踐、陷阱、優(yōu)化技術(shù),以及庫(kù)、工具和社區(qū)組成的生態(tài)系統(tǒng)。其次,編程雖簡(jiǎn)單,卻容易出錯(cuò)。
即使擁有常見的編程經(jīng)驗(yàn)和最好的工具,將想法轉(zhuǎn)換為計(jì)算機(jī)代碼也不是一件直覺(jué)行為。無(wú)論程序員建立了怎樣的直覺(jué),也必須經(jīng)歷反復(fù)使用、即時(shí)反饋和糾錯(cuò)的循環(huán)。每次更換編程語(yǔ)言,你都需要付出代價(jià)。所以,根據(jù)我的經(jīng)驗(yàn),編程語(yǔ)言的選擇很重要,但是一旦做出了選擇,從長(zhǎng)遠(yuǎn)來(lái)看,就應(yīng)該堅(jiān)持下去。
如何選擇編程語(yǔ)言
時(shí)至2022年,我們?cè)谶x擇編程語(yǔ)言時(shí),需要考慮以下幾點(diǎn)。
首先,最關(guān)鍵的考慮因素是語(yǔ)言的適用范圍。如果是特定的領(lǐng)域,必須使用一些特定于領(lǐng)域的語(yǔ)言,則最具普遍適用性的語(yǔ)言是首選。值得慶幸的是,自從Java提出“編寫一次,到處運(yùn)行”以來(lái),運(yùn)行時(shí)和部署便不再是問(wèn)題,成本和許可也不再是制約因素。時(shí)至今日,所有編程語(yǔ)言、運(yùn)行時(shí)以及各種工具基本都可以免費(fèi)獲取。如果某種語(yǔ)言不適合某個(gè)特殊的場(chǎng)合,只能說(shuō)它的流行度不夠,沒(méi)有普及到所有人;要么是因?yàn)橐恍┗疽蛩兀瑢?dǎo)致該語(yǔ)言確實(shí)不適合該任務(wù)。
流行度很重要,我們應(yīng)該選擇擁有強(qiáng)大的社區(qū)、豐富的信息來(lái)源、大量其他程序員可供合作或雇傭的語(yǔ)言。任何不受歡迎的語(yǔ)言都不值得選擇。如果遇到特殊情況,則選擇會(huì)更困難。沒(méi)有任何一種語(yǔ)言能夠適用于所有場(chǎng)景,但在理想情況下,通用的主流語(yǔ)言應(yīng)該足以應(yīng)對(duì)大多數(shù)場(chǎng)景。
最后,我們選擇的編程語(yǔ)言應(yīng)該優(yōu)于大多數(shù)其他語(yǔ)言。即使在2022年,仍有一些糟糕的編程語(yǔ)言,難以學(xué)習(xí)和使用,很容易讓程序員陷入困境。
鑒于上面的陳述,我認(rèn)為實(shí)際上我們并沒(méi)有太多選擇。下面,就讓我們來(lái)看看這些最佳編程語(yǔ)言。
最佳編程語(yǔ)言
JavaScript / TypeScript
編程語(yǔ)言界的JavaScript就像人類交流時(shí)使用的英語(yǔ)一樣。它是最流行、最通用的編程語(yǔ)言,適用于許多不同的場(chǎng)景(瀏覽器/前端、系統(tǒng)/后端、作為擴(kuò)展語(yǔ)言嵌入到許多環(huán)境中)。JavaScript的運(yùn)行時(shí)(V8 / Node / Deno)非常高效,擁有許多出色的工具和龐大的社區(qū)。
TypeScript是JavaScript的超集,引入了強(qiáng)類型和標(biāo)準(zhǔn)工具,正在迅速發(fā)展成為JS編程的默認(rèn)選擇。
Rust
Rust擁有C/C++的所有功能,更易于使用,而且也沒(méi)有太多陷阱。Rust的社區(qū)和生態(tài)系統(tǒng)非常強(qiáng)大且在不斷發(fā)展,工具也很好用。如果你需要的功能Rust都提供了,那它絕對(duì)是不二之選。以前只能使用C或C++的場(chǎng)合,如今也可以選擇Rust。此外,Rust還在建立自己的WebAssembly通用語(yǔ)言(WebAssembly可以說(shuō)是終極版的“編寫一次,到處運(yùn)行”的運(yùn)行時(shí))。
強(qiáng)有力的競(jìng)爭(zhēng)對(duì)手
Python
我使用Python已經(jīng)超過(guò)20年了,可惜時(shí)至2022年,Python依然算不上真正的通用編程語(yǔ)言。原因之一是,Python仍然非常低效,很多注重性能的場(chǎng)合都無(wú)法采用Python。還有一個(gè)原因是,它未能進(jìn)入主流的面向用戶環(huán)境,比如網(wǎng)絡(luò)瀏覽器或手機(jī)。盡管如此,Python仍不失為一種出色的編程語(yǔ)言,而且在數(shù)據(jù)工程/數(shù)據(jù)科學(xué)/機(jī)器學(xué)習(xí)中占據(jù)了重要位置,所以如果你從事這些領(lǐng)域的工作,那么Python絕對(duì)是一門值得了解和熱愛的語(yǔ)言。就目前的情況來(lái)看,Python很可能會(huì)作為數(shù)據(jù)科學(xué)的通用語(yǔ)言繼續(xù)發(fā)展下去,但可能無(wú)法突破這個(gè)領(lǐng)域。
Go
Go是一種非常適合“云”編程的語(yǔ)言。Go語(yǔ)言優(yōu)雅、易于學(xué)習(xí)和使用,擁有出色的社區(qū)、生態(tài)系統(tǒng)和工具。它被廣泛應(yīng)用于云原生領(lǐng)域的核心產(chǎn)品,因此它會(huì)長(zhǎng)期發(fā)展下去。不幸的是,Go并沒(méi)有普遍的適用性,基本無(wú)法用于互聯(lián)網(wǎng)服務(wù)器之外的環(huán)境。此外,由于Go設(shè)計(jì)上的選擇,它在C/C++世界中表現(xiàn)不佳。Go固然好,但如果必須做出選擇,凡是Go能實(shí)現(xiàn)的功能Rust都可以實(shí)現(xiàn),隨著時(shí)間的推移,Go有可能會(huì)被主流系統(tǒng)編程語(yǔ)言取代。
C#/Java
C#及其生態(tài)系統(tǒng)非常出色,你可以用它實(shí)現(xiàn)很多功能。Java的各個(gè)方面都比不上C#,所以我不理解為什么有人會(huì)喜歡它,但Java確實(shí)很流行。C#的應(yīng)用很廣泛,不僅是一種系統(tǒng)和“商業(yè)”語(yǔ)言,現(xiàn)在更是延伸到了移動(dòng)應(yīng)用程序和瀏覽器。強(qiáng)大的運(yùn)行時(shí),偉大的生態(tài)系統(tǒng)。但是,除非你需要C#的一些量身定制的運(yùn)行時(shí)和工具的功能,否則在短期內(nèi)C#很難與JavaScript和Rust競(jìng)爭(zhēng)。
C/C++
根據(jù)林迪效應(yīng),C和C++在未來(lái)幾十年內(nèi)將繼續(xù)流行下去。如果你已是這兩種語(yǔ)言的專家,肯定不愁找工作。如果有這方面的需求,則花時(shí)間學(xué)習(xí)二者也是不錯(cuò)的選擇。否則,選擇Rust更合適。
榮譽(yù)獎(jiǎng)
Swift / Kotlin / Dart
這幾種語(yǔ)言在特定領(lǐng)域占有一席之地。如果需要移動(dòng)UI編程,則這些是不錯(cuò)的選擇。但JavaScript的適用性更普遍,而且也同樣適用于移動(dòng)開發(fā),因此我們更應(yīng)該選擇JavaScript。
LISP(Racket / Clojure)
LISP很特別,即使日常工作沒(méi)有這種需求,也應(yīng)該學(xué)習(xí)一下。Racket 是最先進(jìn)的、非常復(fù)雜的語(yǔ)言(實(shí)際上它是一種語(yǔ)言構(gòu)建工具包)。據(jù)傳,Clojure的功能很強(qiáng)大,因?yàn)樗哪繕?biāo)是JVM,可以使用 Java 庫(kù)。但我不清楚這個(gè)賣點(diǎn)有多大作用。
Haskell / F# / Scala
函數(shù)式語(yǔ)言很重要。在某些情況下,它們是更優(yōu)的選擇。Haskell是函數(shù)式編程的代表。F#具有更好的普遍適用性,因?yàn)樗倪\(yùn)行平臺(tái)是CLR,并且可以使用 .NET 庫(kù)。Scala不是純粹的函數(shù)式,但非常通用,并且在 JVM 上運(yùn)行。
Julia / R / MATLAB
Julia非常適合數(shù)學(xué)領(lǐng)域。R和MATLAB都有各自擅長(zhǎng)的特定場(chǎng)合。不過(guò),在Python主導(dǎo)的數(shù)據(jù)工程領(lǐng)域,這些編程語(yǔ)言恐怕很難幸存下來(lái)。
PowerShell
如果你從事shell編程,那么PowerShell是迄今為止最好的選擇。它適用于所有操作系統(tǒng),所以我們沒(méi)有理由使用任何其他 shell。PowerShell也算是一種通用編程語(yǔ)言,但實(shí)際上在非系統(tǒng)管理之外,沒(méi)有人使用它。
遲暮之年
PHP / 紅寶石 / PERL
這些語(yǔ)言也曾有過(guò)輝煌的歲月,主要是作為網(wǎng)絡(luò)“后端”語(yǔ)言。無(wú)論你如何看待這些語(yǔ)言,如今都不應(yīng)該再在它們身上白花力氣。它們都在走向滅亡。
Visual Basic / VBA
VB 改變了世界,但如今卻被淘汰出局了,無(wú)論是作為通用語(yǔ)言還是作為對(duì)其他程序的擴(kuò)展。在遙遠(yuǎn)的過(guò)去可以用VB實(shí)現(xiàn)的功能,如今都可以用其他現(xiàn)代語(yǔ)言更出色地實(shí)現(xiàn)。
總結(jié)
我喜歡編程語(yǔ)言,而且永遠(yuǎn)對(duì)新語(yǔ)言充滿了好奇。但是,就目前而言,TypeScript是我心目中的C位,而在需要強(qiáng)大的功能和低級(jí)訪問(wèn)權(quán)限的情況下,Rust居第二。我相信,2022年幾乎所有程序員都與我有類似的看法。