前端开发编程语言的过去、现在和未来

自我介绍

我是贺师俊。之前多次担任QCon的前端开发和编程语言专题的出品人和讲师,也是本次GMTC大会的编程语言专题的出品人。

对待编程语言的态度

关于编程语言我首先想讲一个事情,就是讨论编程语言时,我们经常会看到两种非常极端的态度:

一种就是“XXX语言最好!”XXX可以是任何一个语言,比如PHP🤪。如果讨论的时候你不附和他的观点,就可能给你一个鄙视脸。

另外一种,就是“所有语言都一样!”当我们讨论不同语言的特性优劣,他也会给你鄙视脸。

虽然这两种态度看似截然相反,但这两种态度的结果是一样的,就是讨论继续不下去了,所以你看各种技术群里涉及编程语言的讨论往往要么曲高和寡,要么迅速以“PHP是最好的语言”或者“语言都一样”这样毫无意义的废话(按照复读机队形)收场。排除调侃和灌水的成分,本质上,这两种极端态度背后的原因是类似的——人们总是高估短期,低估长期。

编程语言,以我的看法,不太可能是项目成败的决定性的因素。但是长期来看,编程语言,作为程序员创造软件的核心工具,会影响团队长期的生产力。这是我们为什么应该摒弃那些极端态度,投入一定的精力去研究和发展编程语言。

前端语言的特点

今天这个场合是大前端技术大会,所以我们这次讨论前端和移动端开发中的编程语言。以下简称前端语言。

前端语言有自己的特点,也就是和一般非前端领域的编程语言有区别。如果我们观察前端语言,会发现有两个特点:

第一点是,平台高度相关。

什么叫平台高度相关呢?比如说今天我们要在iOS平台上开发新应用,那基本上就得用Swift语言。反过来说,你要在其他平台上开发新应用,就不太可能选择Swift。

相比较而言,后端开发就没有这么强的约束,因为后端服务并不是说非用哪个平台,比如Java、.NET不可,完全可以用PHP、Go、Python、Ruby等各种语言写,百花齐放。就算用Java或.NET平台,也有多种语言可选。而前端开发中,语言和平台具有强相关性,整个技术栈的选型中,选平台和选语言是一而二二而一之事。

这会造成一些困扰。比如说因为你看到了Flutter这个平台非常好,所以你就用它,可你也一并选择了Dart语言,但是很有可能你其实不是特别喜欢Dart语言——尤其如果我们已经很依赖Kotlin/Swift语言所提供的某些高级语言特性时。也有另外一种情况,比如,假设你的团队一直使用JavaScript/TypeScript,并对整个JS/TS生态里已经有很大的投资(如积累了大量库,自行研发了许多工具等),所以不想放弃这样一个技术栈,那在这样的情况下,很遗憾你现在就没有办法选择Flutter平台。所以我们会发现,在前端开发过程当中就有这个技术栈选型上的约束:编程语言跟平台高度相关。

第二点是,领域高度重合。

如果我们讲前端语言,不管哪一个语言,它其实都是以前端应用这样一个特定的领域为首要目标,所以问题领域是高度重合的,面临的挑战也是共通的,比如都要处理界面和交互。许多其他编程语言就不一样,它们各自有一些独特的特点,擅长不同的领域。比如说Erlang就很适合用来开发分布式系统,它本来的设计目标就是针对这种场景,所以有针对性的、独特的并发模型和容错机制等。再比如说像大数据、人工智能领域就首选Python。而Go就常见于网络服务层、云设施。甚至像Coq这样偏学术的语言,因为其具有形式证明能力,被运用于金融、区块链等领域。所以虽然都是通用编程语言,但不同语言可能有非常鲜明的特点,主攻领域有很大不同。而前端语言,虽然各平台有各自的语言,但其主攻领域就高度重合。

前端语言的过去

从这两个特点伸发开去,我们来看看前端语言的过去、现在和未来。其实这个标题有点大,这里只能把个人在这方面的观察和思考跟大家分享一下,希望能引发大家的思考。

先看一下过去。这里讲的“过去”,是比较近的过去,更早的就不讲了,比如最早移动开发是用J2ME,这个就太古早了。这里我们还是以智能手机为起点,也就是Android、iOS时代。

过去三个主要平台的语言,在Web就是JavaScript(下简称JS),Android就是Java,iOS就是Objective-C(下简称OC)。

为什么前端很少用其他编程语言

那么是不是可以用其他编程语言呢?

在Web平台确实还有不少其他语言,各种altJS(可编译到JS的其他替代性语言),以前一度CoffeeScript也挺流行。但是从整个发展来看,虽然我们总是可以用其他语言编译到JS,然而随着ES6的兴起,总体趋势还是重新回归了JavaScript。

Android用Java,理论上Java平台上的其他语言也可以用,比如Groovy、Scala、Clojure等,但是这样一种实践,也不是特别流行。

为什么呢?原因可能各种各样。比方说工具链不匹配。像构建,Scala用sbt而Android用Gradle。不是说不能解决,但需要花费开发者额外的精力。还有些问题可能就是无解的。比如,当你用另外一个语言,它通常有自己的标准库。对于前端和移动端来讲,你发布的应用,用户要通过网络下载,就有一个大小的问题。如果你使用其他语言,虽然可以编译过去,但是还要带上他的库,就使整个包变大。额外的runtime库造成发布包变大,对后端来说倒是无所谓,前端就很敏感。尤其是一些大公司的app非常复杂,由多个团队共同开发,其包的大小限额也是要在多个团队里分配的,即使其中一个团队认为引入新语言有极大的好处,但你怎么能说服所有团队为业务代码以外的代价买单?非常困难。

为什么CoffeeScript能一度流行,很大原因是它选择了聚焦于语法改进,不需要任何额外runtime库的路线。然而这也限制了它的发展,当ES6也很大地提升了语法便利性之后,CoffeeScript的价值就大大下降,走向衰落。

实际上Swift也有需要打入runtime库导致应用变大的问题,但是它是Apple的亲儿子,所以大家确信这只是暂时的问题,我们看到最新的Swift 5就解决了这个问题,新版的iOS操作系统已经内置了库。但是想象一下,如果是其他语言呢?

所以总体来讲,虽然存在使用其他编程语言的这个可能,但是在实践上,无论Web、Android还是iOS平台,整个前端来说,我们在真正production里还是比较少采用其他的语言。

但是呢,这些上一代的前端语言确实是存在一些问题的。

JS的问题

什么问题呢?我们先讲JS的问题。主要就是:不适合PITL。

PITL就是programming in the large,大型编程。所谓大型编程,时间和组织两个维度,一个是这个软件或服务有比较长的生命周期,另一个就是这个产品或项目有比较大的团队需要多人协作,满足任何一条,就可以算大型编程。

JS这个语言当年设计的时候,不是为大型编程这个目标设计的,它的初衷,就是为满足在页面上做一点动态效果的需求,所以JavaScript名副其实是一个script(脚本)语言。所谓脚本,很多时候就是一次性书写甚至一次性运行,用后即抛。传统上,脚本语言的设计目标就是方便使用,怎么易用怎么来,所以在很多方面并不适合大型编程,甚至背道而驰。比如说大家都知道JS的弱类型隐式转换问题,像臭名昭著的“三位一体”,虽说是吐槽,但实践上也确实会造成隐患,所以当团队要进行“大型编程”,就必须用linter等工具去给语言的设计缺陷擦屁股。

Java的问题

Java又是什么问题呢?其实Java本身是非常好的语言,到目前为止仍然是我们整个行业最重要的编程语言。

Java的问题就是Oracle。Oracle的各种槽点就不说了,在Android平台来说,最主要的就是和Google的官司。这个是非技术的因素。

技术的因素也有。Java虽好,但有一个问题:尽管Java最初也是“前端语言”(早期设计目标是用于嵌入式设备和Web),但Java最成功的领域是在服务器端。那么这个语言的演化就要考虑服务器端,不能仅仅因为前端和移动端有一些新的需求,就把一个语言特性加进去,而是要考虑更广泛的场景来衡量这样一个特性是不是值得加进去。而我们知道,服务器端跟前端、移动端还是有一些差别,比如说平台更新迭代的速率和普遍性,对backward compatibility的要求,对性能如内存占用、启动时间的需求,等等。这种不同会不会对社区心态产生不同的影响从而对语言的演化也产生影响?这或许是一个可以思考的问题。

OC的问题

Objective-C是非常独特和有趣的语言,它有一个问题是和其他主流工业语言存在一定的gap。最简单的例子,我们看OC代码的命名风格,都是非常的长,比其他语言长得多。为什么那么长呢?一个原因是其代码风格就鼓励自说明的、接近英文自然语言表达的、完整到有时觉得冗长的名字。另外一个是,OC中,参数名也是命名的一部分,方法名字和后续参数名合在一起构成整个的命名。最终效果就是在其他语言中字符串替换方法名就是一个简单的“replace”,而在OC中则是“stringByReplacingOccurrencesOfString:withString:”。加之OC的词法比如括号运用也和其他语言不太一样,完整一个语句大概长这样:

newString = [oldstring stringByReplacingOccurrencesOfString:@"oc" withString:@"swift"];

虽然说,坊间流行“Java和JavaScript的关系是雷锋和雷峰塔的关系”,但Java和JS的切换其实并没有那么难,至少语法是很接近的。但是从Java、JS这样的主流工业语言切换到Objective-C就障碍比较大,光语法和命名这些比较表面性的东西就已经没那么容易适应了。

当然OC还有其他的问题,像性能、内存安全性等。但我个人认为OC和其他主流语言的过大差异或许是最大的问题。

前端语言的现在

我们看一下现在前端编程语言的状况,可以看到很明显的变革。在Web平台,大量新的应用全线转用TypeScript,还在维护的老项目迁移到TypeScript的案例也不在少数,尤其是在大厂,这个趋势更加明显。在Android平台,则是Kotlin,最近的统计是超过50%的Android程序员已经切换到了Kotlin。而在iOS,当然就是Swift。除此之外,我们还有了一个新的平台,就是Flutter,它引入了一个“新”语言:Dart。

我们发现,所有的端,这些平台,都在比较短的一段时间内,从老的语言进化到新一代的语言。这不知道是不是巧合(实际上巧合背后往往是有原因的)。

前端语言平台钦定

这些新语言,与一般的编程语言有什么特别的不同呢?首先,我们之前已经提到过平台和语言的关系。你会感觉到前端的编程语言似乎都被平台所“钦定”。比如Kotlin就是Google宣布作为首要支持的语言。Swift是Apple主推的。TypeScript虽然说微软没法“钦定”它,但TypeScript本身是JS和类型系统的结合,而JS是Web平台唯一语言,让TypeScript也附带获得了“钦定”。TypeScript在享受JS的红利的同时,也通过更好的工具支持反哺JS生态。整个JS生态正逐步进化为“JS/TS”生态。最后还有Dart,这个语言其实已经诞生好几年了,在Web和Android平台上寻求发展接连受挫之后,终于获得了Flutter平台“钦定”从而起死回生。总之,前端各平台上所有这些新语言和上一代语言一样,我们感觉都是“钦定”。

编程语言的跨端和全栈

虽然是平台“钦定”,但是我们可以发现,所有这些语言他们都不满足于被“钦定”的那一亩三分地,它们都在走向跨端和全栈,突破单一平台。

像JS/TS,Node.js在全栈开发上已经很多年,很成熟了。跨端方面,则有Cordova、React Native、Weex、NativeScript等各种跨端方案。再说Dart,其实Flutter“钦定”了它必须能跨端。Dart既有standalone VM,又可编译到JS和Native。基于native,可以跑在Android和iOS上,基于dart2js则有Flutter for Web,standalone VM则给予其全栈的潜力。Kotlin语言在语言设计之初也设定了多target的目标,可以编译到JVM、JS和Native,所以无论跨端还是全栈也都没有问题。相对来讲,Swift在跨端方面较弱,尽管技术上说Swift经过LLVM编译到native,你当然也是可以用Swift写Android应用的,社区也有这样的尝试,但Apple对此缺乏动力,不太可能像Dart和Kotlin那样有官方支持。但是全栈道路是没有障碍的,Apple还建立了SSWG(Swift Server Work Group)来推进。此外Swift也可能会官方支持编译到WASM来支持Web平台。总之,各语言都在不断发展其跨端和全栈的能力。

JS/TS仍然是跨端和全栈能力最强的语言

这些语言当中,总体来看,JS/TS仍然是跨端和全栈能力最强的语言。Web自不必说,Android、iOS系统也都是带有完整的JS引擎的,至少语言本身的标准库是无需额外成本的。也因此JS本身就是一个编译target。基本上现在所有的新语言都把能够编译到JS作为必须达成的目标。如果一定要说JS的弱点的话,可能是缺乏编译到Native的能力。虽然JS引擎高度优化,但在极致性能上仍然没法跟Native比。但我们现在有WebAssembly!JS引擎同时也是WASM引擎。当我们发现有性能瓶颈,就可以使用其他语言如C、C++、Rust等编译到WebAssembly从而获得基本接近Native的性能。这样在基于JS的解决方案上就补上了这一环。

从前到后易,从后到前难

谈到跨端和全栈,前面讲的都是出身前端或移动端的编程语言。当然我们也有其他的新的老的语言。那些语言不是说不想做前端,我们发现很多其他语言都尝试过往前端发展,但是都不是很成功。我们可以看到一门编程语言要扩张领域,从前到后比较容易,从后到前就比较难。

这个可能有很多原因了,像之前讲到的非平台钦定语言可能需要额外的runtime库就是一个问题。还有比方说架构思想的差异。语言的发展还是首先来自于开发者的需求。“从前到后”的开发者,通常倾向于把前端和后端视作对等,因而采用前后端分离的架构。而对于“从后到前”的开发者来说,出发点可能反而是希望消除前后端分立,把前端作为一个位于整个系统边缘的表现层来解决。从历史上看,这种架构实施起来难度较大,很难平衡用户体验需求和抽象复杂度。虽然这其实并不是语言的锅,但会影响大家对语言适用领域的看法。当然可能还有很多其他原因,我这里不具体展开了,大家可以考虑后面有一些什么样的因素。

新一代前端编程语言趋同

我们前面看到了不同的新的前端和移动端的编程语言,当然它们像它们的前辈一样,仍然会有许多不同,因为在整个发展过程当中,一定要考虑之前的、建立在上一代编程语言上的技术资产。但是总体来说,我们可以发现,这些新一代前端语言是趋同的。

语法

我们发现,新一代前端语言的语法比原来更加接近。很多较新的特性几个语言都有,而且语法是基本一样的。

比如说“?.”运算符,提供了更简洁的null-safe的属性访问语法,Kotlin、Swift和Dart都支持。JavaScript也早就有提案,但是语法上有一点争议,所以一直没有进展,这也导致TypeScript团队拒绝实现该特性。不过本月(2019年6月)月初时终于达成一致意见,维持了和其他语言一样的“?.”符号,提案也进入到了Stage 2。我们也可以期待尽快能在TypeScript中使用该特性。

语法的趋同上,最明显的当然是OC到Swift的转变。Swift现在跟其他语言是非常像的。包括命名风格,比如前面提到的那个OC的“stringByReplacingOccurrencesOfString:withString:”,在Swift里就简化了,现在是“replacingOccurrences(of: …, with: …)”,并且借助extensions特性, 程序员可以自行加入和其他语言完全一致的“replace()”,这样写出来的代码和其他语言就几乎完全一样了。

类型安全

另外所有新的语言都进一步加强类型安全。

首先就是静态类型支持,像TS就给JS把静态类型加上了。OC本来有很多地方也是动态类型的,现在Swift则是完全的静态类型。静态类型的引入,对大型编程是非常有帮助的,比方说让IDE能具有准确的代码提示和自动完成的能力。

尽管是静态类型,但我们并不需要在每个地方都显式声明类型,因为这些新语言都大量使用自动类型推导,既获得了静态类型的好处,也没有劣化开发者体验。很多时候我们甚至觉得,如果你的代码大部分是胶水代码或应用逻辑,尤其是编写UI的时候,好像和用动态类型的语言写出来的代码没有什么区别。

这些新一代语言也都支持泛型。不是所有静态类型的语言都支持泛型,比如Go(当然Go 2.0也要加入泛型)。泛型对于网络服务开发来说或许不一定那么重要,但对于应用开发我认为还是相当重要的。我们日常开发中经常使用容器类,泛型对于容器类的类型安全来说必不可少。

还有non-null和null-safe,前面提到所有编程语言都在加入这方面的特性,底下可能各有不同,比如Swift是基于Optional type而没有传统的null,JS/TS则有两个null值(null和undefined)🤪,但面上的语法基本一样,编程体验也很接近,可以说,都比较好的解决了空指针这个“价值亿万美元的错误”。

语言设施

除了语法、类型安全之外,我们还可以看到语言设施也有一个趋同的倾向。举个例子,异步编程设施对于前端开发非常重要,因为不管什么平台,为了保证用户体验,共同的准则都是不能在UI线程里做非常heavy的操作导致用户操作无响应,这种情况下,异步编程,处理很多异步的操作变成必然要做的事情了,而且面临的挑战其实在各个语言里面也非常类似,比方说callback hell。所以整个异步编程方面,可以发现新语言也越来越接近。比如很多都是基于Promise或者叫Future模式。JS/TS和Dart里是作为标准库,Swift下有PromiseKit。进一步的,语言可能加入async/await语法,JS/TS、Dart都支持,Swift也已经有提案,未来也会加入async/await特性。Kotlin的机制虽然可能略有不同,是基于coroutine的,但从编程体验上看,也是大同小异。

开发体验

此外,前端语言在开发体验上也互相借鉴。上一个演讲里董博士(Flutter团队高级研究员:董韬博士)提到了Hot Reload,这个过去只有Web前端开发者可以享受到的便利性。但是现在其他端也都引入了。比如在Android上Instant Run,Flutter也提供了Hot Reload,而SwiftUI发布时也同时发布了Xcode Previews。所以整个体验是往这方面靠拢的,因为这个对于我们开发效率确实有提高的。

为什么会趋同

为什么我们在语法、类型安全、语言设施和开发体验等方面会出现趋同?

第一,这几个前端语言的生态位是类似的。我们也有一些很有特色的语言,比如Elm、Clojure、ReasonML那样一些语言,都很有特色,但是生态位跟我们前面讲的官方“钦定”的前端语言不一样,它们在源流上本来就基于相对小众的语言(Haskell、Lisp、OCaml),转到前端领域,就算有大厂加持(如ReasonML是facebook推出的),也只能是所谓niche,局限在特定人群(各流派函数式编程语言的爱好者,能力很强的小团队等)。我们前面讲的新一代平台“钦定”的前端语言,尽管现在可能只是在一个平台上,但都是希望占据更大的市场的。它们都要抢占“主流工业语言”的生态位,所以他们是以主流工业语言为标的来设计和推广的。在这样相同的生态位上,所有的东西就会有一个相似性。要成为主流工业语言,你要吸引已有的主流工业语言的使用者,尽量靠近传统的Java、C#,无论是语法、语言特性、编程范式(对OOP的支持)之类的,就比较重要。

第二,整个前端开发者的流动,本来原来是Web前端开发者,现在做iOS或者Android的开发,或者反过来。这个趋势非常非常明显。我举一个例子来讲,以前我们觉得React只是一个Web前端领域的框架,但是React Native推出之后,有一次我去听React Native的技术分享,会场里有一半以上是iOS和Android开发者。因为这个流动的趋势,我们希望阻碍流动的因素越少越好。作为新一代“主流工业语言”的竞逐者,互相参考借鉴,就响应了这样一种开发者流动的需求。

第三,本质因为都是前端开发,问题领域非常相似,比如前面已经讲过了异步编程的共性。既然它的问题领域是相似的,最后的结果可能也会殊途同归。因为好东西大家都想用嘛。在这一点上我想特别讲一下UI DSL。

UI DSL

JS/TS下的JSX/TSX,Kotlin的Anko,Swift刚刚发布的SwiftUI,最后Flutter,这四个东西,其实在很多地方是非常相似的,即所谓UI-as-code。当然我们直觉上会认为它们有一些不同,比如我们觉得JSX是类XML/HTML,而后面几个才是真的语言本身的代码。确实表面上是有这个差别。

为什么在JS/TS出现JSX,主要原因是JS/TS受到很多既往的限制,不太适合在语言内部做出一个好用好看的DSL,所以干脆把大家熟悉的XML/HTML语法引入作为语法糖,用转译器去转换成等效代码。(当然这个方式其实是有一些后遗症的,比如很难进入语言标准。)相反,Kotlin在设计之初就把DSL作为设计目标,所以可以做出很好的DSL。Swift也具有类似的能力。

Flutter就比较有意思了,Dart在很多方面很保守,对内部DSL设计不是特别友好,所以跟JS/TS有相似问题(当然还是比JS/TS好多了),它做出的DSL不是很好看。不过董韬博士(Flutter团队高级研究员)已经讲了,已经做了很多改进,包括有一些语言层面的改进,使得DSL能看起来更清晰,另外通过IDE的改进,也能够弥补在语言层面没有办法完全解决的问题。

无论如何,UI-as-code这个趋势是共通的。

怎么达到UI-as-code?有两个方向:

一个是专用DSL代码化,或者说是外部DSL内部化。Web端就是这样,原来的HTML变成了JSX/TSX,原本声明式的外部DSL变成可以用代码操纵的对象。包括我们传统CSS,在这个体系里就倾向于CSS in JS。方案各种各样,但是都是要把本来单独分离的CSS或内嵌于HTML的CSS转为内嵌到JS/TS代码里,由JS/TS代码引用和管理。这方式好不好我们另说,但思路就是这样。

另一个,就是JS/TS以外的语言,它们是命令式代码的声明化,这个就不多解释了。

这两个方向,它们在本质上是殊途同归的。最后达到的就是一个声明性DSL和命令式程序的结合,或者叫统一。

前一段,React推出了React Hooks,可以理解为新一代的UI DSL。像我前面讲趋同,在React Hooks上也马上可以看出来,React Hooks推出之后,我们整个Android社区、Swift社区,包括Flutter社区都可以看到马上有开源项目跟进,尝试把React Hooks的模式搬运到各个平台上。所以也印证我前面讲的为什么会趋同的那三点,各个平台和语言互相影响和借鉴。React Hooks作为现在还是一个新的、我认为也比较先进,确实有很多优点的解决方案,我们期待它会不会在不久的将来被吸收到各个平台的官方方案里面去。

说到React,我也要提一下Vue。在不久之前在VueConf上面,Vue作者尤雨溪介绍了Vue 3未来的设计。比如Vue 3的function-based component也借鉴了React Hooks的方式,但是跟React Hooks还是有一些区别的,比如很“巧合”的避免了React Hooks设计上的一些代价。还有一个比较重要的,我们前面讲UI-as-code,似乎大家都不用模板了,但是这里有一个误解,template和UI-as-code就一定是矛盾的吗?在Vue 3当中做的一个事情就是template和render做了完美结合。template是真正声明式的东西,可以做彻底的静态分析。前面讲的现在的那些UI-as-code虽然是声明式的,但是本质仍然是code,很多地方没法静态分析,比如确定到底哪个局部发生了变更,依赖关系是什么,这可能是未来要去解决的问题。Vue已经先一步拿出了解决方案。Vue的设计还是引导开发者尽量使用template,这样可以充分利用静态信息,同时局部可以自定义render实现,获得完全的可控性和灵活性。这样Vue可以精确计算出UI的变更,使得所需的底层更新操作最小化,获得惊人的性能提升。具体这里不展开了,大家可以去自行了解,这是目前我看到最先进的方案。

所以说单独的UI-as-code本身是不是一定是最好的、最终的答案?可能不是。但我们也不用担心,如果是真的好东西,那么按照我前面讲的,一定会流传到各个语言和平台。只不过,我在这里提出来,这也许是一个机会,我们把握到这个趋势,也可以去参与和推动这个趋势,也就是参与创造未来。

未来

这就讲到了未来。

预测未来是很危险的事情,90%的概率是错误的。但是我还是愿意冒险谈一谈。

首先有一点是未来这些前端语言会越来越相似。因为前面讲了看到这样的趋势,我觉得从很多方面,一方面确实看到确实是这样,不同特性如果被证明好的语言特性,或者被证明对开发者是有价值的,那么它会在不同前端语言和平台当中普及开来。

但是另外一方面也要注意,毕竟语言还是有差异的。以下是一些个人的感受。

激进、中庸和保守

我们感觉Swift是相对比较激进的语言,被人诟病每年都要学一门新语言。当然Swift发展到现在这个版本,基本之前很多东西已经相对稳定下来了,可以认为从狂飙的阶段进入相对稳定的阶段。但是总体上我们还是会感觉,Swift这个语言和这个社区是不怕做一些激进的改变的。比方说SwiftUI也许未来也会有很多的改动。

Kotlin就是一个相对比较中庸的语言。可能很多人不同意我的看法,觉得Kotlin也加入了非常多先进的语言特性,完全不亚于Swift。确实是这样。但是这个中庸是和Java整个平台下其他一些语言比较,Kotlin非常注重可以复用Java现有的东西。所以我觉得它的秉性其实是比较中庸的,中庸在这里不是贬义词,是一个很好的特性。

Dart这个语言,就是一个相对比较保守的语言,比如引入所有语言新特性的方面有点慢,甚至有时候是迟缓。有一些Kotlin、Swift早就有的特性,Dart就欠奉,一直到确信开发者非常需要这特性,才会提上议事日程。不过保守不一定是坏的,保守有保守的好处。Dart语言的最初作者本身就是搞VM的,要确保某个语言特性在VM可以高效运行,对于语言设计来说就有比较高的约束。这也许是Dart保守秉性的源头。

TS/JS未来面临的挑战

最后是JS/TS,它和前面三个语言有一个重要区别。前面三个语言虽然都是open source的,但都是有主导者的。Swift主导者是Apple,Dart主导者是Google,Kotlin则是JetBrains。TypeScript虽然是Microsoft开发的,但TS的未来很大程度上并不取决于Microsoft,除了类型系统之外,其他都是JS带过去的。JS这个语言现在是一个比较典型的委员会语言了。这里面其实没有一个非常强势主导方,而是委员会的一群人一起去合作改进的。那么这群人很多方面就会非常不同,我们看JS新特性的提案讨论,既有非常激进的动议,也有非常保守的,有一些特性不愿意加。人多嘴杂,事情就难办。而且TS/JS其实本质上是从前一代语言(传统JS)发展而来,它不像其他几个语言是全新的,它还是要背负着不少历史包袱。JS社区又非常复杂,差异性很大,我们怎么知道怎么判断社区在总体上的需求和好恶?尤其是当一些设计导致利害冲突时,谁能代表真正社区的心声?所以在这里我就打了一个问号,可能也是出于JS/TS需要面临的长期挑战。因为整个语言在发展上,缺乏主心骨,可能面临委员会语言的弊病。

未来的变数

技术上的一个变数是WebAssembly。前面说过WASM可以补齐JS/TS的短板。但另一方面,各个其他语言也可以由此进入JS/WASM引擎可以运行的所有的端。所以这是一个你中有我我中有你的情况。会不会有新老语言借此改变前端语言的战局呢?好像也很难说一定没有可能。

这里还想讲的未来当中一个变数,就是我们前面没有提到的小程序。前面提到了Web端、Android、iOS端,但对中国开发者来讲,还有另外一个端是小程序,而且准确讲他还不是一个端,它是有很多很多不同家的“小程序”、“快应用”等,它们虽然非常相似,但是又有一些不同,所以在这个地方其实也是一个非常大的挑战,同时我也觉得这也是一个很大的机遇。

多端的方案,对中国开发者来讲,大家希望把小程序一并解决。前面董博士介绍了Flutter for Web,可能对于中国开发者来讲还期待有Flutter for 小程序。如果小程序不能解决,在很多的选型场景下就没有办法选择Flutter。

选型

所以我们前端的技术栈选型,最大的约束点,第一,平台需求到底跨多少端,这是非常重要的因素。前面讲的就是Flutter在这上面有小程序这个缺门。第二,就是团队的技术背景,这个团队本身熟悉一个怎么样的开发工具和开发语言的生态。我们基本还是从这两点进行出发。

中国的公司和产业是否会诞生新语言

最后讲一点想法。

我们前面看到的新语言,像Dart是Google做的,像Swift是Apple做的,TypeScript是Microsoft。这都是大公司。Kotlin的JetBrains不是大公司,但也得到了Google的大力支持。Google和Microsoft其实还搞其他语言,Go啊、C#、F#等。还有像Facebook,首创了JSX,搞了flowtype(很类似于TS),还搞了ReasonML、Hack(PHP版的TypeScript)等。其实我觉得中国现在头部的科技公司,从规模来讲,可以认为已经不亚于他们了,但是我们到现在来讲都没有看到任何一个中国的头部公司设计和开发编程语言。当然研究性质的语言可能是有的,比如华为应该是有的,但性质和我们这里讨论的不太一样,不是面向一般程序员的。我们各个公司确实已经做一些自己的平台,比如小程序、快应用等,但是没有搞过编程语言,至少好像没有大张旗鼓的搞,可能内部有偷偷的搞,我们没怎么看到。

大家可能会有一个疑问,说为什么一定要做语言呢?

确实,很多时候我们用即有的语言就可以了,但是我们反过来思考一下,Google、Apple、Microsoft、Facebook等公司为什么搞新编程语言呢?注意,大家不要想当然觉得人家有强势平台所以可以做这个事情,也不要以为他们总是能搞成功的。实际上有成功的例子,背后一定有更多失败的。比如Facebook的Skip,就是内部放弃之后才开源出来的,ReasonML其实也不见得很成功,说不定未来哪天也就放弃了。像Dart我前面也提到过,要不是Flutter,恐怕也没有什么前途。于是有些人会说,连他们都失败了,我们干嘛要做呢?所以好像过去这些年对于搞编程语言这件事情,我们一直是抱持极大怀疑态度的。但是显而易见,你如果永远不去尝试,那么你永远不可能有成功的可能。

当然,我们总可以选择不做这个事情,我们就使用别人做的编程语言,我们搭便车,一样可以享受语言发展的红利。但是这里有一个问题就是,如果我们永远是搭便车的方式,我们就比较会缺乏在编程语言维度解决问题的能力。咱们中国整个头部公司的工程师其实已经都非常厉害了,经过这么多年的锻炼,在我们整个工具箱里面我们有很多的解决的方法,比如库、比如框架,都没有问题,还有很强的hack能力,但是编程语言这个维度我们可能是相对比较缺失的一个维度,整个工具箱里缺了一个重要的工具。

什么叫编程语言维度解决问题?举两个例子。

第一个例子就是前面圆心(阿里前端委员会主席)提到了,曾经有一段时间经常做各种JS Module的方案。我们可能觉得,用CommonJS方案解决就可以了。这个方案不是不好,实际上是一段时间里整个生态的选择。但是CommonJS还是有一些问题,它是通过类似库和框架的方式解决的,是一个runtime的模块机制,而我们知道今天的ES6的模块机制,以及大部分其他语言里的模块机制,是静态的,不是runtime的。静态机制有好处,具体这里不展开讲。这里只是用来说明一点,像模块机制,本质上要在语言层面才能得到很好的解决。其实,我们曾经一度很接近这个语言层面的机制,比如玉伯(蚂蚁金服研究员)当年做的Sea.js的方案,虽然长得和CommonJS一样,实际上Sea.js是一个静态模块机制,本质上和CommonJS是不同的。Sea.js已经接触到了语言层级。

另外一个例子就是async/await,好多年之前有一个中国程序员赵劼,就是著名的百万年薪程序员组合“温赵轮”里的老赵,他其实是一个C#程序员,他不是JS程序员,但是他把C#/F#中的async/await引入了JS,开发了Wind.js。

所以呢,我觉得整个中国程序员,就像圆心讲的,我们的思考,我们的能力其实没有问题的,但是似乎有些事情,比如编程语言维度解决问题这一点,没能进入到我们公司的、产业的主线上。老赵做的这个东西非常好,但是一直没有流行起来,我们还是等了3、4年,等到外国人做的JS编译器加入了这个东西,我们才说,哦,这是个好东西。Sea.js的情况可能好一点。

当然,这些局部的成果离开发一个完整的新编程语言也还离得比较远,但是我们可以慢慢来,比如DSL和IDE,我们的产业界已经在这方面有实践。前面我们讲到小程序,其实里面就有很多DSL,每个公司都做了一遍(笑)。这个地方我们已经积累了一些经验,或许从一个小的DSL逐渐将来可以走向更加完整或者通用的编程语言。另外IDE,前面也提到了,现在整个编程语言其实不是单单语言本身,而是包含了整个工具链。微信开发者工具就是个例子。如果我们深入各个公司了解一下,我们发现各个公司各个团队很多都是在做IDE上的投资,包括前面圆心提到这事阿里未来的重点方向。

还有,我们不一定现在马上做一个新编程语言,但是我们可以开始贡献现有的编程语言社区,比如董韬博士提到中国程序员对于Flutter的贡献,已经非常显著了。

最后是参与编程语言标准制定。比如ECMA的TC39是JS语言的标准委员会,阿里已经在考虑加入,走这样一个流程。我在这边也同步一下我们360的情况,上周我们已经向ECMA提交了会员申请,并准备加入TC39工作组。希望无论是阿里还是360,或者是其他中国公司,能够代表中国的产业界和开发者去参与这样一个事情。

总结

工欲善其事,必先利其器。对于我们程序员来讲,编程语言就是我们一个最重要的器。所以,我希望今天演讲的内容,包括一些可能比较零碎、松散的想法,能够给大家在整个前端开发和编程语言方面带来更多的理解与思考。谢谢大家!