趙海平與張巨集波談程式語言

語言: CN / TW / HK

本文整理自 位元組跳動 Web Infra 團隊與 稀土掘金技術社群 合辦的 大咖面對面 活動,本期嘉賓邀請到了 ReScript 作者張巨集波,與位元組跳動資深架構師、HipHop for PHP 作者趙海平。正文內容有刪減。

程式語言學到什麼程度才算掌握?

張巨集波:一般來說一門語言,它的本質就是提供一些 API 和執行時,讓你可以在之上做一些事情。比如說 Objective-C 你可以寫 mobile ,另外一個,它是給你提供一種抽象機制,它可以讓你做模組化的程式設計。

對 JavaScript 來說,它的最大的優點就是你的無處不在。但這個語言它本身它設計的時候是比較草率,一開始設計的時候不是都說是一週設計出來的,所以它從抽象的這種程度來說並不是設計一個非常完美的語言。所以接下來這十幾年都在給這語言打補丁。

有一本書叫 JavaScript The Good Parts,其實我覺得像學習 JavaScript 這種語言的話,你只要學習它的 Good Parts, 那個 Bad Parts 你就可以忽略掉,或者說只有等你真正需要的時候,才去瞭解為什麼。而且現在因為有了 TypeScript,很多 Bad Parts 你都可以避免掉。所以你只要學對應你有用的抽象就好,就不要去 focus 在一些犄角旮旯裡面去。

如果你需要表達某種抽象機制的話,學經典的設計模式就可以了。就不用糾結在 JavaScript 語言本身,比如說最早的 JavaScript 他有 prototype chain 那個鏈,是一個 proto 屬性,那些很細節的東西,我覺得不需要去糾結。

PHP 的未來在哪裡?

趙海平:我覺得對於一個語言來說,就像我想去了解這個部落格一樣,一定要去了解它的來龍去脈,瞭解它的 context。我們瞭解 PHP,也是需要了解它的來龍去脈,瞭解它的 context ,你們要知道那個 PHP 那個它產生的那個年代是上個世紀末的時候,恰好那個時候 web server 如雨後春筍一樣子,出現了很多的這個 WebSite ,網際網路才開始出現,那是一個很激動人心的時刻。與此同時,所有的東西都非常的原始。我不知道抱怨 PHP 不好的人,他們有沒有聽過一個詞叫 LAMP , 這個詞很關鍵很重要。這個 LAMP 是什麼?英語裡面這個詞叫燈,但實際上它是 Linux, Apache、MySql、PHP 四個的縮寫,LAMP 是 fundamental software for Internet。

這個叫鼻祖,你明白了嗎?你現在抱怨雅虎做多麼的不好,對不起,雅虎是 Internet 的鼻祖。那你說他為什麼不改呢?是因為我們有太多的東西依附他,依靠他,去改的話牽一髮動全身。

所以實際上,不是說負責 PHP 的人看不到這些缺點,你可以去敲她的門說,聽著,我告訴你 PHP 這些地方不好,但是我都知道,但是我改不了好吧,所以這要去理解和包容。你要去理解現在發生了什麼,你要去 understand 說 PHP 它是怎麼來的?然後它為什麼有今天這樣的一個狀況?然後其實有很多的時候,甚至對每一個 feature 你覺得這個東西設計得好奇怪,好莫名其妙,你就要去讀他的歷史,為什麼會是這樣子的?你就會發現有可能就有一個很有趣的故事。

所以這就是我想說的,你對程式語言有多少的愛,你的包容心就會有多強。所以說看到一些不好事情的時候,最後反而變成一個很有樂趣的事情,其實有很多的故事。所以說我不反對任何的說 PHP 不好的一些評語,確實還有很多的設計是不好的。但是與此同時我又能夠理解他,我又知道他為什麼不好。

PHP 的缺點,我舉一個可能大家詬病最多的就是它有很多的所謂的 semantics 語義,它定義很模糊。比如說這個 false,很多的 value==false 這個怎麼去解釋?這個除非你要去查那個表,否則的話你自己是猜不出來的。

這種設計,它就所謂的比較 hacky,沒有那個 Java 那麼 formal 。所以我有時候建議,如果你去學校學一個語言的話,可能你應該去學 Java ,為什麼呢?因為 Java 它的設計非常 formal 、正規,非常的 canonical ,canonical 就是很正規軍、正規化,然後它有很多的東西是來龍去脈,很有道理的。然後 PHP 的很多的設計,有一些隨意性,有一些 on the fly ,比如我今天在這寫的時候,就這樣定義,結果一下子就錯了好多年。

所以如果你要問我 PHP 哪裡不好的話,你到網上去一搜,你可以看得到很多這樣子的 example。但是就像你剛剛說的那一句話,我覺得我們原來在 Facebook 的時候,大家一邊在抱怨 PHP 不好,一邊迅速的把這件事情給做完了。明白我說的意思嗎?PHP 寫東西實際上是很快的,因為他很 hacky 所以說他可以很快的把這個網頁攢出來。

Facebook 沒有那麼在意程式碼一定要寫得非常的完美,為什麼呢?是因為當時大家也不知道網站應該建成什麼樣子,也許第二天程式碼就被刪掉重新寫了,所以你去過度的 polish 打磨程式碼,有的時候反而還不如快速寫完,叫所謂快速迭代的過程。

所以 PHP 就跟 Facebook (當時的)研發的模式恰好很匹配,大家也沒有那麼強的抱怨。但是如果換另外一個公司,你很在意那個程式碼的這個穩定性和持續性。比如說你是一個搞金融的公司,你很在意程式碼的可維護性,那可能 PHP 就不是一個好的語言。

Golang 的精華和糟粕

張巨集波:其實我用過一些功能,但我不是特別精通,我覺得 Golang 還是一個不錯的語言。

比如它的 interface define,據我所知,是第一個主流語言把 structuraltyping 用在工業語言當中,當然其實很多學術語言已經有了。然後他的編譯很快,我也非常贊同,編譯速度是很重要的。對我來說,Golang 比較缺失的就兩塊,一個是泛型,但是現在泛型已經加上去了。然後另外一塊,我覺得現代語言都應該有代數資料型別和模式匹配。但是 Golang 很遺憾,Golang 作為一個新生語還是沒有引入,所以我覺得也是比較遺憾的。

趙海平:對,我覺得對,因為巨集波是那個學語言出身的,所以說這個觀點都是這個帶有強烈的這個 academic (學術)的特點。我說一下 industrial(工業)的觀點,互補一下。

Golang 我不知道為什麼,說糟粕、精華本身帶有強烈的感情色彩。然後我覺得也要像 PHP 一樣去理解和了解它的來龍去脈:為什麼 Google 要去做 Golang 呢?是因為我們在這個過去的五到十年裡面,就發現了實際上絕大多數的公司在寫什麼呢?在寫分散式計算。特別常見的情況是我調你、你調它,它調它。你們知道微服務為什麼現在這麼 popular 因為現在就是這樣子,團隊大了之後拆分 engineering task 拆分完了之後,我就要 RPC 呼叫對吧?所以說這個變成了這個這個很多的 application 就變成了所謂的 IOintensive 的這種形式,就是他不是一個簡單的計算,他可能也許跟 20 年前的話,很多的軟體都是在做大量的計算對吧,沒有太多的 RPC 因為都在一個機器上進行。

後來這拆分了之後,計算的比重相對來說就沒有那麼多了,更多的是 IO,那麼 IO 的話,問題就來了,你的執行緒模型是什麼?好吧,那 Java 說讓我來解決這個問題,Java 說我們就有就 thread 對吧。後來結果發現 threadJava 那個 thread does't work 就不行,給你 50 個 thread 是 OK 的,給你 1000 個 threadJava 那個就不要跑了,這個速度太慢了。所以的話大家在沿著這個 IO 如何讓 IOintensive 的 application 跑的好,然後就有很多的想法和思想出來。

然後最後就發現了。天,原來我們一定要用比如說 epoll 這種模型,要搞 user thread ,相當於在 user space 裡面使用者的這個輕量級的這種 thread 來解決 IO 的問題。Go 就是迎合這個趨勢。那我想讓其他的人都能夠很快的去跳到這個模型上,所以他才把 goroutine (協程)這個概念變成了 first classcitizen ,所以其實人家的本意是在這裡。

當然你可以抱怨 Go 有很多的不好的地方,你如果問我,我可能會挑他的 error handing 做的不太好,他沒有 exception ,我寫 C++ 的話,我覺得 exception 還是很有用的,讓很多的那個程式碼變得很整潔,那 Go 的話就不行,一定要 return 一個 error 。

那這個也不是說錯誤,這個程式語言就跟這個人生一樣,只有選擇沒有對錯。千萬不要一棒子打死人家這個選擇,天哪你沒有 exception 那你就是錯的。只是人家這樣選擇的目的是因為 exception 是一個性能損耗點,那我要快,為了極致的效能,對不起你們所有寫 Go 語言的人,你們自己累一點,你們自己去 check 一下那個 return value ,就不要說太簡單了,所以這就是一個取捨。

所以一個 tradeoff,你可以不同意這個取捨。但是沒辦法,這個語言是人家發明的,人家幫你做了一個決定,所以說你去理解了人家 decision 背後的為什麼之後,可能就會心平氣和很多。你不能夠去說什麼精華或者糟粕,這麼多人一起的集體的創造的一個智慧的結晶,他不太可能有糟粕在裡面,只是會有選擇,可能是你不喜歡的選擇這個是完全有可能。

WebAssembly

張巨集波:業界有一個錯誤的觀點, WebAssembly 可以讓各種語言以接近原生的速度來跑,這個是不對的。因為你只能說對底層語言 C++ 或者它編譯出來的部分,是可以跑得很快的。但是你說 JavaScript 或者 PHP 理論上也是可以編譯到 WebAssembly 的,但它照樣是很慢的,因為它的速度快與慢是由於語言的特性決定的。不是說你有了 WebAssembly ,它就突然就變快了。

然後 WebAssembly 它現在是有兩種模式的,一種是跟 Web 平臺沒有關係,他只是作為一個通用的 IR(中間語言) 你任何語言都可以編到這個 IR 以後,你可以在雲端也可以計算,然後它比傳統的 IR 有個好處,首先它是一個跨平臺的,編譯成 WebAssembly 之後就可以扔到任何一個作業系統上跑。另外一個,它設計的初衷是有安全考慮的,它可以很快做安全的校正,然後可以提供一個很好的 sandbox 這是在後端的使用。但前端主要是,它可以把一些計算比較複雜的應用那個移植到 C++,然後把它編譯成 WebAssembly 然後再進行呼叫。比如說一些傳統的編碼解碼這些方面。

現在用的比較多的成功的案例是把一些老的 C++ 程式搬到 Web 平臺上。最近的一個比較成功的案例,Adobe 的 photoshop 應該是用 C++ 程式碼寫的,我可以把很多已有的程式碼直接通過一些這個新的技術把它編到你的 Web 平臺。這樣你可以把一些老的應用提供給更多的使用者。

這是 WebAssembly 的兩種使用方式。回到 ReScript 和 PHP,因為 ReScript 是一門靜態語言,它是可以編譯到 WebAssembly 的,而且呼叫是可以走通的,已經有人試過了。像 PHP 或者 JavaScript 這種特別動態的語言,目前來說,即使把它變到 WebAssembly 裡,效能是得不到提升的,你需要把它的很多語言特性給裁掉,像 TypeScript 有人做了一個子集,做成了一個 AssemblyScript ,它是非常非常小的子集,然後你才可以把它編譯成有效的 WebAssembly。所以 WebAssembly 不是一個讓你任何東西突然就變快了。

趙海平:我們語言小組對 WebAssembly 也感興趣,感興趣的點其實是在他的 inter-operability 上,我不管你以前的語言是什麼,這個編譯完了之後,然後一下就可以這個相互呼叫了。這個是我們現在在看的點,但是才剛剛開始看,理解並沒有那麼深刻。其實但是我覺得這個 WebAssembly 這個事兒挺有意思的,隨著到 2021 年,其實就會發現我們有很多種語言了,實際上你看 WebAssembly 也好,LLVM 也好,其實要回答的問題基本上都是怎麼樣讓這麼多語言相互之間可以很好的融合,或者說節省 engineering effort ,我覺得這個趨勢可能真的還挺好的。

聊聊現在大熱的 Rust

趙海平:位元組內部其實有很多的人喜歡 Rust,內部的使用者群特別大,很多人加入到群裡。然後他們會問我喜不喜歡 Rust,我特別謹慎,我特別怕說錯話,因為我要說 Rust 好與不好的話,特別怕變成一個站邊的問題,所以我保持比較謹慎的態度。但是可能最終我們是要去支援 Rust,畢竟要鼓勵大家去嘗試一些新的東西。不過,Rust 的優點和缺點其實都是很明顯的。在目前看來(2021 年),Rust 的優點的話,毫無疑問比如說它沒有 Garbage Collector(垃圾回收機制) ,它是強調 object ownership(所有權),可能寫的時候稍微費一些勁,但是在執行的時候沒有 Garbage Collection 這個障礙,然後就可以很高效。然後其他比如說像做一些 system program 的話比較安全,優點特別的明顯,都非常認可,但是缺點也很明顯。比如說這個編譯速度比較慢,基礎庫也不完整,然後這個可能 IDE 的 support 也不是特別的好。然後還有一個是,它不是很好學,learning curve 還是挺高的,你想要搞明白那個 object ownership 可能你還真得要是一個高手,你可能才能夠完全徹底的去理解。所以我覺得屬於一個我們去跟隨著這個社群不斷的進步的這麼一個狀態。

張巨集波:我補充一下,Rust 和 ReScript 它有共同的淵源。如果你喜歡 Rust 的話,你也會喜歡 ReScript,因為他們都是從那個 ML(metalanguage) 語言出來的,就是元語言。Rust 最早的編譯器是用 OCaml 寫的,ReScript 就相當於 Rust 把 GC(前文所指的 Garbage Collection) 給拿掉,你不需要去學那些 lineartypes 那東西確實比較繞。如果你沒有 C++ 或者 C 的基礎,可能都很難理解。所以如果你喜歡 Rust 的話,你也會喜歡 ReScript,而且 ReScript 因為有了 GC ,模式匹配、使用者體驗可以做得更好。然後回到最開始的問題,Rust 現在很火,而且生態也在慢慢的建成,但 Rust 客觀來說它的門檻是很高,主要是在 linear types ,它的型別已經 leak 到了使用者,即使我只是你的 library user (庫的使用者),我也必須要了解這個東西怎麼回事,門檻還是相對比較高。

程式語言解決的本質問題是什麼?

張巨集波:程式語言的本質我剛才已經提到了,它就提供兩種功能,一種是提供一系列底層的 API, 比如像 GLSL 就 shade language 我可以讓你操作 GPU 這是它的一個最本質的東西,因為有了這個平臺你能實現自己想要的功能。然後另外一個他提供一種抽象機制,像最早的彙編,它沒有 function ,所以它基本上沒有任何抽象機制,你很難做大規模的模組化程式設計。

所以對於你使用者來說,首先我要了解他的 API ,像大部分工業語言,其實它的提供的抽象能力是差不多的。比如你學了 C++ ,你再去學 C# 是很快的,因為它的抽象模式基本上是差不多的。但是如果你學一下學術語言,像 haskell 或者那個 prolog 它給你帶來一種全新的體驗,它會提供另外一種抽象方式。

趙海平:巨集波是正兒八經科班出身學語言的,所以一定要聽巨集波關於這個問題的看法。

如果問我的話,我可能也是更多的從 industrial(工業)的角度去思考語言。我覺得這個問題問的還挺好的,雖然很深刻,但是其實也有它實用的一面。你想想看,其實我們現在所說的語言,它確實可以讓我們幫助我們去做很多的事情,那就是說去 instructcomputer 對吧,告訴計算機幫我們做什麼事情。

但是實際上有兩件事情,目前它做的不是特別的好,或者說還做不到,或者說不在語言的範疇之內,但實際上是值得問這個問題,為什麼他不在語言的範疇之內呢?哪兩件事情呢?一個就是資料庫,database,其實我也是想問巨集波這個問題,對於做學術研究的人, database 是一個 camp,programming language(PL)是一個 camp ,然後這兩個 camp 的人實際上思考問題的方式, PL 的人更多的是從 CPU 的角度去思考問題, database 的人更多是從 data 的角度去思考問題。彼此都認為自己是可以囊括這個全世界的。

所以說實際上你看語言實際上跟 database 它對接地方,很多的時候並不是說非常非常的完美。所以很多的時候一個語言在訪問資料庫的時候,很多的有所謂的 data binding ,這個 problem 要解決。有 serialization(序列化)、deserialization(反序列化),有好多類似這樣這方面的問題,它並沒有把資料庫、database 或者 data 的很多東西囊括在語言裡面。

當然有這樣的嘗試,但是貌似這個目前還是兩大 camp, database 和 PL。為什麼這兩個 camp 的人不能在一起,為什麼不把兩個東西搞在一起?所以你如果能夠問這個問題的話,其實可以回答說什麼叫語言的本質是什麼。

還有一件事情就是分散式計算。目前還沒有一個語言說它能夠凌駕在眾多的機器之上。我去寫一個程式,這個程式咔嚓一下,在幾千幾萬個機器上跑。那當然我們有類似的系統,比如說 map-reduce,但是它沒有上升到語言這個層面。它沒有說我今天位元組只需要一個語言就好了,你們都不要去這寫這個寫那的什麼 server 一個 client 一個,就一個語言,我寫一個程式下來,然後一下 deploy 到所有不同的機器上跑,這也是一個好問題,為什麼語言就不去做這件事情呢?這也是在回答語言的本質的問題,也沒有好的答案。

那在今天其實我們去看,這個問題是有意義的。為什麼呢?因為我們微服務實際上是一個很大的呼叫鏈。一個服務調其他的,其他服務又調更多的服務,它是一個樹狀的 call tree,它是有 dependency 的,但是還沒有一個語言能夠把完整的把整個的 call tree 給描述出來,如果真的可以描述出來的話,那 compiler (編譯器)就可以去優化這棵樹。我怎麼樣去優化讓整個的呼叫是最 efficient 的,可是語言並沒有回答這個問題,現在情況是,留給大家自己去做一些排程也好,或者自己去做一些優化也好,或者用 WebAssembly 讓這個不同的微服務之間有一些 inter-operation。

所以我不知道怎麼回答這個問題,我的答案是我不知道,因為我也不滿意,我也不覺得我充分理解了語言的範疇,這個邊界在哪,也許再隔個五年,十年之後語言又被重新定義了,都是有可能的,對吧?任何問這麼有意思問題的人都可以去嘗試,都可以去創新。

普通業務程式設計師要參與語言開發嗎?

趙海平:因為我不是學語言出身,所以我是當年在 Facebook 被 push 去做這件事情:我們機器不夠用了,所以我們必須要優化。然後毫無疑問大家知道 PHP 慢,慢的意思就是消耗 CPU 消耗多,需要的機器比較多,因此大家都想著要去優化 PHP ,所以我是被逼無奈去做的語言,所以至少這是一個途徑,被逼著去做語言。

當時我們在美國的語言小組中,大家總是沾沾自喜,因為當時彼此之間流傳著一句話:凡是做 compiler 的人,都是最好的的 programmer 。這句話就是有吹牛的地方,但是它也有它有道理的地方。

當你透徹地理解了 compiler 的時候,你在寫程式的毫無疑問就很通透。你知道怎麼回事了,你知道怎麼寫是特別 efficient 的,不僅僅是把它給寫完了,功能實現了就完了,你一寫的話就能夠寫得讓機器也很喜歡,不僅僅是人很喜歡,機器一執行起來速度也很快。所以可能也許瞭解語言毫無疑問對於對每一個人技術成長是有幫助的,但是它也是一個很高成本的事情,鑽到語言裡面去學。你們也知道在上學的時候,compiler 那個課可能就是最難通過的一門課,很艱深,你究竟願意不願意花這麼大的一個 cost 去做這個努力,我倒真覺得不一定。有的時候也還是要隨緣一些。我真的有一天我特別想要去學的話,我就去學。但是如果今兒這邊閒待著沒有什麼事情,就逼著自己去學,也不見得就那麼有道理。巨集波可能是特別的喜歡語言?

張巨集波:對我大部分還是認同的。我客觀來說,一般人不一輩子可能都不會去設計一門通用語言,或者是設計出來一門大家用的通用語言。但是我覺得,學習語言設計和編譯器還是有點用的。首先對於編譯器,它有前端和後端,如果你對後端比較瞭解,那你優化一些底層上的東西是很給力的。拿我個人來說,我對 OCaml 就非常非常熟,我基本上我看到那個 OCaml 的原始碼,我就能知道它的那個彙編程式碼是什麼樣,那我就知道它的效能是怎麼樣的。如果你對後端比較瞭解的話,你去做效能調優是比較有用的。

然後前端其實也有很多有用的地方,像我們在業務中有很多超程式設計,就像谷歌的 protobuffer 你要去寫很多的程式碼生成,這一塊是需要用到編譯器前端知識。所以編譯器前端、後端也是可以在業務中有使用的價值。而且我想再補充一句,其實現在的編譯器的門檻已經不是那麼高了。如果你要做後端的話,你有 LVM IR 就不用自己去查彙編的說明,你只要查一些通用的 LVM IR ,生成到 LVM IR 就可以得到機器碼了。然後前端有很多通用的 parser generator 讓你去 fit ,有很多各種各樣的工具可以用,也是很容易就可以上手的,有的時候不需要了解它的理論也是可以用的。

影片回放地址可在公眾號對話方塊回覆關鍵詞 程式語言 獲取。