免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
一文解決現代編程語言選擇困難:命令式編程

本文轉載自 InfoQ,作者:Ilya Suzdalnitski 翻譯:蓋磊

如何了解某種編程語言的優(yōu)缺點?某種編程語言是否適用于我的項目?面對此類問題,如果求助于搜索引擎,輸入“最佳編程語言”,結果會羅列一堆文章,涵蓋 Python、Java、JavaScript、C#、C++、PHP 等,并且大多對各語言的優(yōu)缺點表述得模棱兩可。我本人很不喜歡去讀此類文章,因為其中許多部分內容的表述并不到位,缺少實戰(zhàn)借鑒意義,而且行文生硬。因此我試撰本文來做一次深入總結,去偽存真。

本文概述了當前廣為使用乃至可能小眾的現代編程語言,力圖做到盡量客觀、值得一讀,并不帶個人偏見。各語言按推薦程度從低到高依次列出。

謹記,并不存在所謂完美的編程語言。有的語言非常適用于后端 /API 開發(fā),而有的語言則非常適用于系統(tǒng)編程。

文中按當今兩大通用語言家族分類,即 C 衍生語言家族和元語言(Meta Language,ML)衍生語言家族。

對于開發(fā)人員而言,編程語言只是工具箱中的工具,更重要的是如何選擇合適的工具去完成工作。我衷心希望本文有助于讀者選取適合自身項目的編程語言。做出正確的選擇,可降低數月甚至數年的開發(fā)工作量。

哪些編程語言特性值得關注?

很多編程語言排行榜文章,主要對比的是語言使用廣泛度、開發(fā)人員收入期望值等因素。在軟件領域,雖然大規(guī)模的社區(qū)和生態(tài)系統(tǒng)的確有所裨益,但語言的使用情況并非好的排行指標。本文另辟蹊徑,采用的評判依據主要考慮語言的強大之處和不足之處。

為表示所列語言的推薦程度,文中使用“贊”(??)、“否”(??)和“尚可”(??,即不贊也不否)三種 emoji。

那么應該比較哪些特性?換句話說,除了語言使用廣泛性,還有哪些特性更能代表語言的受歡迎程度?

類型系統(tǒng)(Type System)

類型系統(tǒng)倍受大量開發(fā)人員的青睞,這也是為什么 TypeScript 之類的語言日漸大行其道。在我看來,類型系統(tǒng)去除了大量的程序錯誤,更容易實現重構。但是否具有類型系統(tǒng),只是本文考慮的部分評判因素。

支持類型系統(tǒng)的編程語言,最好同時具備類型推斷(type inference)。一個好的類型系統(tǒng),不用明確地標出函數簽名(function signature),也能支持對大部分類型的推斷。不幸的是,大多數編程語言提供的僅是基本的類型推斷功能。

更進一步,支持代數數據類型(Algebraic Data Types,ADT)的類型系統(tǒng)評分更高。

強大的類型系統(tǒng),還應支持高級類類型(higher-kinded types)。高級類類型是對泛型(generics)的更高階抽象,支持編程人員在更高的抽象層上編程。

盡管大家對類型系統(tǒng)寄予厚望,但還有一些比靜態(tài)類型(static typing)更重要的特性。因此在選擇一門編程語言時,不能只看是否支持類型系統(tǒng),

學習難度

即便編程語言是完美無瑕的,如果一位新手上船需要前期投入數月甚至是數年的精力,那么又會有多少人使用呢?另一方面,很多編程范式需要數年的時間才能逐漸完善。

好的編程語言需對新手友好,掌握它們不應花費大量學習時間。

空值

我將 1965 年創(chuàng)建的空值引用(null reference)稱為“億萬美元錯誤”。當時,我正設計首個完全類型系統(tǒng),用于面向對象語言中的引用。目標是確保所有對引用的使用是絕對安全的,并由編譯器自動執(zhí)行檢查。我無法克制添加空值引用的誘惑,完全因為空值引用非常易于實現。近四十年來,這一設計導致了不計其數的錯誤、漏洞和系統(tǒng)崩潰,可能造成了數十億美元的痛心損失。

— 空值引用的創(chuàng)立者 Tony Hoare

為什么說空值引用是不好的?因為空值引用破壞了類型系統(tǒng)。一旦默認為空值,那么就不能依靠編譯器檢查代碼的有效性。任何空值都是一枚隨時可能引爆的炸彈。如果沒能想到所使用的值的確為空值,那么會產生什么后果?會出現運行時錯誤。

function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1);}capitalize('john'); // -> 'John'capitalize(null); // 未捕獲類型錯誤:不能讀取為空值的屬性“charAt”。

為確保所處理的值并非空值,開發(fā)人員必須對運行時做手工檢查。即使是靜態(tài)類型語言,空值引用也破壞了類型系統(tǒng)的很多優(yōu)點。

function capitalize(string) { if (string == null) throw 'string is required';return string.charAt(0).toUpperCase() + string.slice(1);}

運行時檢查也稱為“空值防護”(null guards),在現實中可歸為一種不良的編程語言設計。一方面,引入樣板代碼破壞了編程風格。更糟的是,它并不能確保我們是否檢查了空值。

好的編程語言,應在編譯時做類型檢查,判斷值的存在與否。

因此,支持空值檢查機制的編程語言應加分。

錯誤處理

捕獲異常并不是一種好的錯誤處理方式。拋出異常本身沒有問題,但僅適用于程序沒有辦法恢復而必須崩潰這類異常情況。異常和空值一樣,會破壞類型系統(tǒng)。

如果將異常作為錯誤處理的首選方式,那么就無法獲知函數是返回了期望值,還是發(fā)生了故障。拋出異常的函數也無法實現復合(Compose)。

function fetchAllComments(userId) { const user = fetchUser(userId); // 可能拋出異常。const posts = fetchPosts(user); // 可能拋出異常return posts // posts 可能為空值,這會再次導致異常。.map(post => post.comments) .flat();}

無法獲取部分數據而導致整個程序崩潰,這顯然并非一種好的做法。盡管我們不希望發(fā)生這種情況,但它的確會發(fā)生。

一種做法是手工檢查是否生成異常,但是在編程過程中可能會忘記對異常做檢查,因此這種做法是非常不可靠的,而且會在代碼中添加大量額外處理。

function fetchAllComments(userId) {  try {    const user = fetchUser(userId);
    const posts = fetchPosts(user);
  1. return posts
.map(post => post.comments) .flat(); } catch { return []; }

目前已有更好的錯誤處理機制,支持在編譯時對潛在錯誤做類型檢查。因此,默認無需采用異常處理的編程語言也應加分。

并發(fā)

當前業(yè)界正處于摩爾定律的末端,即處理器不會再大規(guī)模提速。我們身處多核 CPU 時代,所有的現代應用必須能很好地利用多核技術。

不幸的是,大多數當前在用的編程語言都是設計用于單核計算時代的,本質上并不能有效地支持多核處理。

一種亡羊補牢的設計,是在后期提供支持并發(fā)的軟件庫。但這只是給語言打了補丁,并非從根本上就針對并發(fā)設計,不能稱為良好的開發(fā)體驗。一些現代語言內建了對并發(fā)的支持,例如 Go、Erlang 和 Elixir 等。

不可變性

我認為大型的面向對象程序,需要解決由于大規(guī)??勺儗ο箝g關聯(lián)所導致的復雜圖結構。否則在調用方法時,必須得把握并牢記該方法的功能和副作用。

—— Rich Hickey,Clojure 創(chuàng)建者。

當前的編程工作中,使用不可變值越來越常見。即便是 React 這樣的現代 UI 軟件庫,也考慮使用不可變值。對支持不可變數值提供一等支持的編程語言,我們會給出更高的評判。這完全是因為不可變性避免了編程中出現許多軟件缺陷。

什么是不可變狀態(tài)?簡而言之,就是數據不會發(fā)生改變。例如,大多數編程語言中的字符串。字符串轉為大寫,并不會去改變原始的字符串,而是返回一個新的字符串。

為確保任何事情都不發(fā)生改變,不可變性對上述理念做了進一步擴展。更改不可變數組,總是會返回一個新的數組,而非原始數組。更新用戶名,將返回一個包含更新后用戶名的新用戶對象,并不改變原始對象。

不可變狀態(tài)不做任何共享,因此無需操心線程安全所導致的復雜性。不可變性使得代碼更易于并行化。

不對狀態(tài)做任何更改的函數,稱為“純函數”(Pure)。純函數更易于測試和推斷。使用純函數,無需操心函數體之外事情,可聚焦于函數本身。不用像面向對象編程中那樣必須牢記整個對象圖,這樣極大地簡化了編程開發(fā)。

生態(tài)系統(tǒng)和工具鏈

一種編程語言可能本身并沒有多少亮點,但如果其具有大型的生態(tài)系統(tǒng),這會令語言更具吸引力。具備良好的軟件庫,可以節(jié)省數月乃至數年的開發(fā)工作。

顯著的例子就是 JavaScript 和 Python。

速度

語言的編譯速度如何?程序的啟動速度如何?運行時的性能如何?所有這些都是影響評判中的考慮因素。

誕生年代

盡管并非絕對,通常新推出的語言要比原先的語言更好。只是因為新語言會吸取了前輩的經驗教訓。

 C++    

下面從最糟糕、也可能是計算機科學中最大錯誤的 C++ 語言開始。當然,我并不認為 C++ 是一種很好的現代編程語言。但 C++ 當前依然得到廣泛應用,在此必須提及。

語言家族:C

?? 語言特性

C++ 可稱為糟糕透頂的語言……如果項目局限于 C,意味著不會有任何機會被 C++ 愚蠢的“對象模型”搞砸。

—— Linux 創(chuàng)立者 Linus Torvalds

C++ 中填充了各種特性,力圖無所不能,但在在其中任何一項上都不能說出色。C++ 支持 goto、指針、引用、面向對象編程、操作符重載,以及各種非生產特性。

為什么說 C++ 不好?在我看來,最大問題在于 C++ 頗具年頭了。C++ 是在 1979 年設計的。在當時設計者缺少經驗,關注點發(fā)散,雖然所添加的特性在當時看來是似乎好的做法。C++ 得到了非常廣泛的使用,這意味著為其中支持各種用例而添加了更多特性,導致特性成堆。

?? 速度

C++ 的編譯時間出奇的慢,甚至比 Java 慢很多,盡管與 Scala 不相上下。

但在運行時性能和啟動時間上,C++ 程序表現非常優(yōu)秀。

?? 生態(tài)系統(tǒng)和工具

上圖的推文給出了很好的解釋。C++ 編譯器的錯誤信息對新手并不友好。通常并未指出導致錯誤的確切原因,需要開發(fā)人員花時間查找。

???? 垃圾回收

我曾希望在 C++0x 標準中至少考慮可選地支持垃圾回收,但這在技術上存在問題。

—— C++ 的創(chuàng)建者 Bjarne Stroustrup

垃圾回收從未添加到 C++ 中,而手工內存管理非常易于出錯。開發(fā)人員必須操心如何手工釋放和分配內存。我對使用非垃圾回收語言的經歷記憶深刻,其中大量的缺陷在當前支持垃圾回收語言中可輕易避免。

?? 面向對象編程的失敗嘗試

我提出了“面向對象”一詞,但并沒有沒有顧及 C++。

—— 面向對象編程的創(chuàng)建者 Alan Kay

面向對象編程是一項很好的技術,出現于上世紀六十年代后期,當時 C++ 剛出現。不幸的是,不同于 Smalltalk 等語言,C++ 在實現面向對象編程中出現了幾個致命錯誤,導致好的理念變成噩夢。

好的一方面是,不同于 Java,至少在 C++ 中面向對象是可選的。

???? 學習難度

C++ 是一種復雜的低層(low level)語言,不具備任何自動內存管理機制。由于特性紛雜,初學者必須花費大量時間學習。

?? 并發(fā)

C++ 設計用于單核計算時代,只支持簡單的并發(fā)機制,這還是在近十年中添加的。

?? 錯誤處理

拋出并捕獲錯誤是 C++ 的首選錯誤處理機制。

?? 不可變性

未內置對不可變數據結構的支持。

?? 空值

C++ 中所有引用均可為空值。

評判

C++ 的初衷是成為更好的 C 語言,但這一初衷并未實現。

系統(tǒng)編程是 C++ 的最適合使用場景。但考慮到已具有 Rust 和 Go 等更好、更現代的替代語言,系統(tǒng)完全可以不用 C++ 實現。不管讀者同意與否,我不認為 C++ 具有任何優(yōu)點。

是該終結 C++ 的時候了。

  Java    

Java 是自 MS-DOS 以來計算機領域中最令人困擾的事情。

—— 面向對象編程創(chuàng)始人 Alan Kay

Java 出現在 1995 年,比 C++ 晚了 16 年。Java 是更簡單的編程語言,由此得到廣泛使用。

語言家族:C。

?? 垃圾回收

相比 C++,Java 的最大優(yōu)點是具有垃圾回收,這極大地消除了各類軟件缺陷。

?? 生態(tài)系統(tǒng)

Java 已經存在很長時間,在后端開發(fā)領域形成了大型生態(tài)系統(tǒng),極大地降低了開發(fā)負擔。

?? 面向對象語言

本文不會深入探討面向對象編程的不足。詳細分析可閱讀本文作者的另一篇文章,“面向對象編程:億萬美元災難”。

在此給出計算機科學中一些最為杰出人士的看法:

抱歉,我多年前使用了“對象”一詞。該詞使得很多人聚焦于一個更狹義的理念,雖然更廣義的理念是消息傳遞。

—— 面向對象編程的創(chuàng)始人 Alan Kay

Alan Kay 是對的,許多主流面向對象編程語言并未找準關注點。它們聚焦于類和對象,而忽視了消息傳遞。幸運的是,Erlang 和 Elixir 等一些現代編程語言找準了方向。

受面向對象編程影響的編程語言,會導致計算機軟件冗長、可讀性不好、描述性差、難修改和維護。

—— Richard Mansfield

所有使用 Java、C# 等面向對象編程語言的開發(fā)人員,如果曾具有使用非面向對象編程語言的經驗,對此應深有體會。

?? 速度

大家都知道,Java 運行在 JVM 之上,而 JVM 的啟動速度是出名的慢。我曾看到有運行在 JVM 上的程序耗時 30 多秒才啟動起來。對于現代云原生程序,這是不可接受的。

一個大型項目,如果編譯速度慢,就會對開發(fā)人員的生產效率產生顯著影響。Java、Scala 等 JVM 語言存在同樣的問題。

但從好的一面說,JVM Runtime 的性能還算不錯。

?? 學習難度

盡管 Java 是一種相當簡單的語言,但 Java 以面向對象編程為主,這使得 Java 很難做到優(yōu)秀。編寫一個簡單的 Java 程序可信手拈來,但是掌握如何編寫可靠、可維護的面向對象代碼,則需要十數年的 Java 功力。

?? 并發(fā)

Java 設計于單核計算時代,和 C++ 一樣,僅支持基本的并發(fā)特性。

?? 空值

Java 中,所有引用均可為空值。

?? 錯誤處理

拋出并捕獲錯誤是 Java 的首選錯誤處理機制。

?? 不可變性

未內置對不可變數據結構的支持。

判定

Java 在剛推出時,的確是一種很好的編程語言。但遺憾的是不同于 Scala 等語言,Java 始終專注于面向對象編程。Java 編程嚴重受模板代碼的影響,冗余代碼多。

Java 應該退居二線了。

   C#      

C# 和 Java 并沒有本質上的差異。C# 的早期版本,就是微軟的 Java 實現。

C# 具有 Java 的大部分優(yōu)點。C# 于 2000 年推出,比 Java 晚 5 年,借鑒了 Java 的經驗教訓。

語言家族:C

?? 語法

C# 在語法上一直保持略微領先 Java。盡管是一種面向對象語言,但 C# 在解決模板代碼問題上比 Java 有所改進。很高興看到 C# 每個新版本都能改進語法。例如,添加了表達體函數成員(expression-bodied function members)、模式匹配、元組等特性。

?? 面向對象語言

和 Java 一樣,C# 主要針對面向對象編程。面向對象編程的缺點如上所列,在此不再詳述。下面列出一些知名人士的觀點。

我認為相比函數式語言,面向對象語言中缺失可重用性。問題在于,面向對象語言需要處理其所提供的所有隱含(implicit)環(huán)境。盡管我們想要的只是一根香蕉,但卻得到了一只握著香蕉的大猩猩,甚至是整個叢林。

—— Erlang 的創(chuàng)建者 Joe Armstrong

我完全同意這個說法,相比函數式編程,命令式編程非常難以重用面向對象代碼。

面向對象編程提供了對正確做法的一個反面教材……

—— 計算機科學先驅 Edsger W. Dijkstra

從我自己使用面向對象和非面向對象編程的經驗看,我完全同意面向對象代碼更難以正確實現功能。

?? 多范式(Multi-paradigm)

C# 聲稱是一種多范式語言,尤其是聲稱支持函數式編程,但我并不同意。對函數提供一流支持(first-class functions),并不足以稱之為函數式語言。

那么什么語言可稱為具備函數式特性?應至少內置支持不可變數據結構、模式識別、組合函數的管道操作符、代數數據類型(ADT)等特性。

?? 并發(fā)

和 Java 一樣,C# 創(chuàng)立于單核計算時代,僅提供基本的并發(fā)支持。

?? 空值

NullsC# 中,所有引用均可為空。

?? 錯誤處理

拋出并捕獲錯誤是 C# 的首選錯誤處理機制。

?? 不可變性

未內置對不可變數據結構的支持。

評判

盡管我本人的職業(yè)生涯中主要使用的是 C#,但還是對這種語言評價不高。與對 Java 的評判一樣,我建議讀者尋找更現代的替代語言。C# 在本質上依然是 Java,只是具有更現代的語法。不幸的是,C# 本身并不“sharp”。

Python

Python 早在 1991 年提出,和 JavaScript 并稱當前使用最廣的兩種語言。

語言家族:C

?? 生態(tài)系統(tǒng)

Python 軟件庫幾乎無所不能。不同于 JavaScript,Python 不能用于 Web 前端開發(fā),但大規(guī)模的數據科學軟件庫彌補了這方面的不足。

?? 學習難度

Python 語言非常簡單,初學者數周就能上手。

?? 類型系統(tǒng)

Python 是動態(tài)類型的,因此談不上需要類型系統(tǒng)。

?? 速度

Python 是一種解釋性語言,性能慢。對性能有嚴格要求的程序,可使用 Cython 替代原生的 Python。

相對于原生語言,Python 的啟動也相當慢。

?? 工具

對比其他的現代編程語言,難免會對 Python 的依賴管理頗為失望。目前存在 pip、pipenv、virtualenv、pip freeze 等工具。相比之下,JavaScript 只需要 NPM 這一種工具。

?? 并發(fā)

Python 在創(chuàng)建時并未全面考慮并發(fā),僅提供基本的并發(fā)特性。

?? 空值

Python 中所有引用均可為空。

?? 錯誤處理

拋出并捕獲錯誤是 Python 的首選錯誤處理機制。

?? 不可變性

未內置對不可變數據結構的支持。

評判

很不幸,Python 并不提供對函數式編程的支持。函數式編程非常適合處理數據科學所面對的問題。即便是在 Python 擅長的 Web 爬蟲領域,Elixir 等函數式語言表現更好。

我并不推薦使用 Python 完成大型項目,該語言在構建中并未充分地考慮軟件工程。

如果有更好的選擇,不推薦在數據科學之外使用 Python。在數據科學領域,Julia 可能是 Python 的很好替代,盡管相比 Python 而言,Julia 的生態(tài)系統(tǒng)近乎不存在。

   Rust   

Rust 是一種現代低層語言,最初設計用于替代 C++。

語言家族:C

?? 速度

運行快速是 Rust 設計所秉持的初衷。在編譯性能上,Rust 程序要慢于 Go 程序,但運行時性能比 Go 稍快。

?? 空值

至此,本文推薦列表中終于出現支持現代空值的語言了。Rust 中沒有 null 或 nil 值,開發(fā)人員使用 Option 模式。

// 源代碼: https://doc.rust-lang.org/rust-by-example/std/option.html// 返回值或者是 T 類型的 Some,或是 None。enum Option{ Some(T), None,}// 整數除法不會出錯。fn checked_division(dividend: i32, divisor: i32) -> Option{ if divisor == 0 { // 錯誤表示為 None。None } else { // 結果使用 Some 封裝。Some(dividend / divisor) }}// 該函數用于處理失敗的除操作。fn try_division(dividend: i32, divisor: i32) { // 與其他枚舉一樣,Option 值可模式匹配。match checked_division(dividend, divisor) { None => println!('{} / {} failed!', dividend, divisor), Some(quotient) => { println!('{} / {} = {}', dividend, divisor, quotient) }, }

?? 錯誤處理

Rust 的錯誤處理引入了現代函數式方法,使用特定的 Result 類型,聲明可能會產生失敗的操作。Result 模式非常類似于 Option 模式,只是在 None 的情況下依然有值。

// 結果或者是 T 類型的 OK 函數值,或是 E 類型的 Err 函數值。enum Result<T,E> { Ok(T), Err(E),}// 存在失敗可能的函數。fn random() -> Result<i32, String> { let mut generator = rand::thread_rng(); let number = generator.gen_range(0, 1000); if number <= 500 { Ok(number) } else { Err(String::from(number.to_string() + ' should be less than 500')) }}// 處理函數的結果。match random() { Ok(i) => i.to_string(), Err(e) => e,

?? 內存管理

在本文列出的現代編程語言中,Rust 是唯一不提供垃圾回收的。Rust 迫使開發(fā)人員去考慮如何實現底層的內存管理,這影響了開發(fā)人員的效率。

?? 并發(fā)

由于 Rust 中缺少垃圾回收,因此實現并發(fā)是相當困難的。開發(fā)人員必須考慮“裝箱”(boxing)和“釘住”(Pinning)。這在具有垃圾回收機制的語言中,通常是自動完成的。

?? 不可變性

未內置對不可變數據結構的支持。

?? 低層語言

作為一種低層語言,開發(fā)人員的生產效率無法其他高層語言相比。同時,語言的學習難度明顯增大。

評判

Rust 非常適合系統(tǒng)編程。盡管比 Go 更復雜,但 Rust 提供了強大的類型系統(tǒng)。Rust 提供了現代的空值替換和錯誤處理方法。

為什么本文將 Rust 排在 TypeScript 和 JavaScript 之后?Rust 是一種設計用于系統(tǒng)編程的低層語言,并非后端和 Web API 開發(fā)的最適合選項。Rust 缺少垃圾回收機制,未內置對不可變數據結構的支持。

TypeScript

TypeScript 語言編譯為 JavaScript,通過對 JavaScript 添加靜態(tài)類型,意在成為一種“更好的 JavaScript”。類似于 JavaScript,TypeScript 同樣用于前端和后端開發(fā)。

TypeScript 由同是 C# 設計者的 Anders Hejlsberg 設計的,因此代碼看上去非常類似 C#,可認為是一種用于瀏覽器的 C#。

語言家族:C。

?? JavaScript 的超集

TypeScript 將自己定位為 JavaScript 的超集,這有助于人們采用。畢竟大多數人對 JavaScript 耳熟能詳。

但作為 JavaScript 的超集,更多程度上是一種缺點。這意味著 TypeScript 繼承了 JavaScript 的全部問題,局限于 JavaScript 所有的不良設計決策。

例如,應該沒有開發(fā)人員喜歡 this 關鍵詞吧。但 TypeScript 依然刻意原封照搬。

再有,其類型系統(tǒng)時常令人感到奇怪。

[] == ![]; // -> 為真NaN === NaN; // -> 為假!

換句話說,TypeScript 具有 JavaScript 的所有缺點。一種糟糕語言的超集并不會變身成為一種優(yōu)秀的語言。

?? 生態(tài)系統(tǒng)

TypeScript 完全分享了 JavaScript 龐大的生態(tài)系統(tǒng)。這是其最大優(yōu)點。特別是相比 Python 等語言,NPM 非常好用。

缺點在于,并非所有的 JavaScript 軟件庫都可在 TypeScript 中使用,例如 Rambda/Immutable.js 等。

?? 類型系統(tǒng)

個人感覺,TypeScript 的類型系統(tǒng)毫無亮點。

好的一面是甚至提供對 ADT 的支持。例如下面給出的差別聯(lián)合(discriminated union)類型:???????

// 源代碼來自 https://stackoverflow.com/questions/33915459/algebraic-data-types-in-typescriptinterface Square { kind: 'square'; size: number;}interface Rectangle { kind: 'rectangle'; width: number; height: number;}interface Circle { kind: 'circle'; radius: number;}type Shape = Square | Rectangle | Circle;function area(s: Shape) { switch (s.kind) { case 'square': return s.sizes.size; case 'rectangle': return s.heights.width; case 'circle': return Math.PI * s.radius ** 2;

下面是使用 ReasonML 實現的同樣代碼:???????

type shape = | Square(int) | Rectangle(int, int) | Circle(int);let area = fun | Square(size) => sizesize | Rectangle(width, height) => widthheight | Circle(radius) => 2piradius;

差別聯(lián)合類型是在 TypeScript 2.0 中增添的,TypeScript 的語法尚未企及函數式語言的高度。例如,在 switch 中的字符串匹配易于出錯,編譯器無法在大小寫錯誤時給出警告。

TypeScript 僅提供基本的類型推斷。此外在使用 TypeScript 時,any 關鍵字的出現頻次難免過高。

?? 空值

TypeScript 2.0 添加了對不可為空(non-nullable)類型的支持,使用編譯器選項 --strictNullChecks 啟用。但使用不可為空類型并非編程默認,也并非 TypeScript 的慣用做法。

?? 錯誤處理

TypeScript 中,使用拋出和捕獲異常處理錯誤。

?? 新的 JavaScript 特性

新酷特性首先在 JavaScript 中得到支持,然后才是 TypeScript。實驗特性可使用 Babel 在 JavaScript 中得到支持,而在 TypeScript 中則無此功能。

?? 不可變性

TypeScript 對不可變數據結構的處理,要顯著劣于 JavaScript。JavaScript 開發(fā)人員可使用支持不可變性處理的軟件庫,但 TypeScript 開發(fā)人員通常必須依賴原始數組或對象展開操作符(spread operator),即寫入時復制(copy-on-write)。???????

const oldArray = [1, 2];const newArray = [...oldArray, 3];const oldPerson = { name: { first: 'John', last: 'Snow' }, age: 30};// 執(zhí)行對象深拷貝(deep copy)非常繁瑣。const newPerson = { ...oldPerson, name: { ...oldPerson.name, first: 'Jon' }

正如上面代碼所示,原生擴展操作符并不支持深拷貝(deep copy),而手工擴展深度對象非常繁瑣。大型數組和對象的拷貝的性能也非常不好。

但 TypeScript 中,readonly 關鍵字非常好用,用于定義屬性是不可變的。雖然如此,TypeScript 要對不可變數據結構提供很好支持,依然需要很多工作。

JavaScript 提供了一些操作不可變數據的很好軟件庫,例如 Rambda/Immutable.js。但是,實現此類軟件庫對 TypeScript 的支持并非易事。

?? TypeScript 對比 React

相比 Clojure 等從設計上考慮到不可變數據處理的語言,在 JavaScript 和 TypeScript 中不可變數據的處理相對更為困難。

—— 原文引用自 React 官方文檔

繼續(xù)說缺點。前端 Web 開發(fā)推薦使用 React。

React 并未針對 TypeScript 設計。最初,React 是針對函數式語言設計的,本文稍后會詳細介紹。二者在編程范式上存在沖突,TypeScript 是面向對象編程優(yōu)先的,而 React 是函數優(yōu)先的。

React 中,函數參數 props 是不可變的;而 TypeScript 中,沒有內置提供適用的不可變數據結構支持。

在開發(fā)中,TypeScript 相比 JavaScript、React 的唯一優(yōu)點是,無需操心 PropTypes。TypeScript 是否是 JavaScript 的超集?這取決于開發(fā)人員的認識。至少我認為是的。做為超集的最大優(yōu)點,是可接入整個 JavaScript 生態(tài)系統(tǒng)。

為什么 JavaScript 的超集語言備受關注?這與 Java、C# 廣為采用是同樣的原因,是因為背后有市場營銷預算充足的大廠在提供支持。

評判

盡管 TypeScript 常被認為是“更好的 JavaScript”,但我依然評判其劣于 JavaScript。TypeScript 相比 JavaScript 的優(yōu)點被夸大了,尤其是對于使用 React 做前端 Web 開發(fā)。

TypeScript 保留了 JavaScript 的所有不足,實際上也繼承了 JavaScript 中數十年積累不良設計決策,的確并非一種成功的交付,

Go    

Go 設計上主要考慮了提高多核處理器和大規(guī)模代碼庫的編程效率。Go 的設計者們當時任職于谷歌,因對 C++ 的共同不喜而得到靈感。

語言家族:C。

?? 并發(fā)

并發(fā)是 Go 的殺手級特性。Go 從本質上就是為并發(fā)而構建。和 Erlang/Elixir 一樣,Go 使用郵箱模型(Mailbox)實現并發(fā)。不幸的是,goroutines 并未提供 Erlang/Elixir 進程那樣的統(tǒng)一容錯特性。換句話說,goroutine 中的異常將導致整個程序宕機,而 Elixir 進程中的異常只會導致當前進程終止。

?? ?? 速度編譯

速度是谷歌創(chuàng)建 Go 的一個重要考慮。有個笑話,谷歌利用 C++ 編譯代碼的時間就創(chuàng)建出了 Go。

Go 是一種高效的語言。Go 程序的啟動時間非常快。Go 編譯為原生代碼,所以運行時速度也非??臁?/p>

?? 學習難度

Go 是一種簡單的語言,如果得到有經驗前輩的指導,新手能在一個月內掌握。

?? 錯誤處理

Go 并不支持異常,由開發(fā)人員顯式處理各種可能的錯誤。和 Rust 類似,Go 也返回兩個值,一個是調用的結果,另一個是可能的錯誤值。如果一切運行正常,返回的錯誤值是 nil。

?? 不支持面向對象編程

雖然這么說有人會反對,但我個人認為,不支持面向對象特性是很大的優(yōu)勢。

重申 Linux Torvalds 的觀點:

C++ 是一種很糟的(面向對象)語言……將項目局限于 C,意味著整個項目不會因為任何愚蠢的 C++“對象模型”而搞砸。

—— Linux 創(chuàng)建者 Linus Torvalds

Linus Torvalds 公開對 C++ 和面向對象編程持批評態(tài)度。限制編程人員在可選的范圍內,是他完全正確的一面。事實上,編程人員的選擇越少,代碼也會更穩(wěn)定。

在我看來,Go 可以回避了許多面向對象特性,免于重蹈 C++ 的覆轍。

?? 生態(tài)系統(tǒng)

一些標準庫的確很笨重。大部分并不符合 Go 返回帶外(out-of-band,OOB)錯誤的自身哲學。例如,有的庫對索引返回 -1 值,而非 (int, error)。還有一些庫依賴全局狀態(tài),例如 flag 和 net/http。

Go 的軟件庫缺少標準化。例如在錯誤時,有的庫返回 (int, error),也有軟件庫返回 -1 等值。還有一些庫依賴標識等全局狀態(tài)。

Go 的生態(tài)系統(tǒng)規(guī)模遠比不上 JavaScript。

?? 類型系統(tǒng)

幾乎所有的現代編程語言都具有某種形式的泛型,其中包括 C# 和 Java,甚至是 C++ 也提供模板類。泛型支持開發(fā)人員重用不同類型的函數實現。如果不支持泛型,那么開發(fā)人員就必須對整型、雙精度和浮點型單獨實現加法函數,這將導致大量的代碼冗余。換句話說,Go 缺失對泛型的支持導致了大量冗余代碼。正如有人指出的,“Go”是“去寫一些模板代碼”(Go write some boilerplate)的縮寫。

?? 空值

不幸的是,即使更安全的空值替代方案已存在數十年,Go 依然在語言中添加了空值。

?? 不可變性

未內置對不可變數據結構的支持。

評判

Go 并非一種好的語言,但也談不上不好,只是不夠優(yōu)秀。使用一種并不優(yōu)秀的語言時需謹慎,因為這可能會導致我們在隨后的二十年中陷入困境。

Will Yager 的博客文章“Why Go Is No Good”

如果你并非供職于谷歌,也沒有面對類似谷歌的用例,那么 Go 可能并非好的選擇。Go 是一種最適合系統(tǒng)編程的簡單語言,但并非 API 開發(fā)的好選擇。原因是因為我們有更多更好的替代語言,本文稍后介紹。

我認為總體而言,盡管 G 的類型系統(tǒng)略弱,但比 Rust 還是略好。Go 是一種簡單的語言,非??欤子趯W習,并且具有出色的并發(fā)功能。當然,Go 成功地實現了做為“更好的 C++”這一設計目標。

最佳系統(tǒng)語言獎授予 Go

實至名歸,Go 是系統(tǒng)編程的理想選擇。Go 是一種低層語言,使用 Go 構建的大量成功項目,例如 Kubernetes,Docker 和 Terraform,證明其非常適合系統(tǒng)編程。

JavaScript

作為當前最流行的編程語言,JavaScript 無需過多介紹。

當然,將 JavaScript 排在 Rust、TypeScript 和 Go 之前是正確的。下面給出原因。

語言家族:C

?? ?? 生態(tài)系統(tǒng)

生態(tài)系統(tǒng)是 JavaScript 的最大優(yōu)勢。我們能想到的所有,,包括 Web 的前端和后端開發(fā),CLI 編程、數據科學,甚至是機器學習,都可使用 JavaScript。JavaScript 可能具有提供任何功能的軟件庫。

?? 學習難度

JavaScript 和 Python 都是非常容易學習的編程語言。幾周就能上手做項目。

?? 類型系統(tǒng)

和 Python 類似,JavaScript 是動態(tài)類型的。無需過多解釋,但是其類型系統(tǒng)時??雌饋砗芷婀郑?/p>

[] == ![] // -> 為真NaN === NaN; // -> 為假[] == '' // -> 為真[] == 0 // -> 為真

?? 不可變性

在 TypeScript 一節(jié)中已經介紹,展開操作符(spread operator)會影響性能,甚至并沒有在拷貝對象時執(zhí)行深拷貝。盡管有 Ramda/Immutable.js 等軟件庫,但 JavaScript 缺少對不可變數據結構的內建支持。

?? JavaScript 并非針對響應式設計的

在 JavaScript 中使用 React,必須借助 PropTypes。但這也意味著必須去維護 PropTypes,這會導致災難性后果。

此外,如果在編程中不加注意的話,可能會導致嚴重的性能問題。例如:

<HugeList options=[] />

這個看上去無害的代碼會導致嚴重性能問題。因為在 JavaScript 中, [] != []。上面的代碼會導致 HugeList 在每一次更新時重渲染,盡管 options 值并未發(fā)生變化。此類問題會不斷疊加,直到用戶界面最終無法響應。

?? 關鍵字 this

關鍵字 this 應該是 JavaScript 中的最大反特性。其行為持續(xù)表現不一致,在不同的情況下可能意味完全不同,其行為甚至取決于誰調用了指定的函數。使用 this 關鍵字通常會導致一些細微而奇怪的錯誤,難以調試。

?? 并發(fā)

JavaScript 使用事件循環(huán)支持單線程并發(fā),無需考慮加鎖等線程同步機制。盡管 JavaScript 在構建時并未考慮并發(fā)性,但與大多數其他語言相比,更易于實現并發(fā)代碼。

?? 新的 JavaScript 特性

相比 TypeScript,新特性能更快地在 JavaScript 中支持。即便是實驗性特性,也可使用 Bable 支持在 JavaScript 中使用。

?? 錯誤處理

Error handling拋出并捕獲錯誤是 JavaScript 的首選錯誤處理機制。

評判

JavaScript 并非一種很好設計的語言。JavaScript 的最初版本僅用十天就拼湊出來,盡管在后期版本中修正了許多缺點。

拋開上述缺點,JavaScript 依然是全棧 Web 開發(fā)和很好選擇。如果加以適當的代碼修煉和分析,JavaScript 是一種很好的語言。

原文鏈接:

These Modern Programming Languages Will Make You Suffer

https://www.infoq.cn/article/9VXbENMFH9Y65VCOI4D1

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
幫你提升 Python 的 27 種編程語言
博采 27 門語言之長,提升 Python 的能力
程序員不糾結:九大非主流頂尖編程語言!
為什么我們不再發(fā)明編程語言了?
一位“老程序員”的反思:C、Python、Java 不可兼得,專心學好一門編程語言就行!
現代編程語言大 PK,2020 年開發(fā)者關心的七大編程語言
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服