本文整理自字节跳动 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 ,有很多各种各样的工具可以用,也是很容易就可以上手的,有的时候不需要了解它的理论也是可以用的。
视频回放地址可在公众号对话框回复关键词 编程语言 获取。