Flow 开源,我们还需要一门新语言吗?

本文最初发表于 Flow 官方博客上。

4 月 26 日, Area9GitHub 上以开源的形式发布了 Flow。Flow 是一个完整的平台,用户能够使用函数式编程语言编写可以在任何地方运行的应用程序。每天都有新的编程语言发布,编程语言已经这么多了,为什么我们还需要一门新的语言呢?

历史背景

Flow 是大约 9 年前构想出来的。那时的世界大不相同,HTML5 还不存在,在浏览器上运行复杂代码的唯一方法是使用 Flash。

而现在 Flash 已经过时了,但作为一家企业,Area9 以前需要一种方法来编写在 Flash 中以及 iOS 和 Android 上运行的软件。我们的自适应学习产品在客户端做了大量的数据处理,为计算出最佳的自适应路径。但是这不会扩展到服务器端的数百万用户。所以我们别无选择,只能使用 Flash,但我们也知道 Flash 最终会消亡。

很长一段时间以来,我们尝试修改 Haxe 来做到这一点,但在那个年代,我们无法让它在内存受限的移动设备上表现得足够好。Haxe 是一种很棒的语言——我们仍然在 Flow 的 JS 运行时中使用它——但事实证明,要让它在第一代 iPhone 和 Android 手机上运行得很好太难了。(从那时起,Haxe 已经取得了长足的进步,现在已经可以很好地工作了。)

但在当时,作为一家企业,我们的选择是有限的。那时候,智能手机上的应用程序还没有真正出现。我们是一家初创公司,我们的客户不会为我们开发的同样东西三次付费。我们明白,移动设备至关重要,因为那就是未来。因此,它让我们别无选择:要么忽视移动平台并在未来失败,要么设法自己解决这个问题。

所以,我们将问题定义为:设计和实现尽可能小的语言,让我们可以完成这个任务。我们的目标是能够安全、快速地为所有主流平台编写复杂但易于使用的漂亮 UI,而这正是 Flow。

历史里程碑

当我们开始在 Flow 中实现我们的学习软件时,扩展并改进了其功能。以下是一些关键的时间点:

  • 2010 年 8 月:第一个 Flow 程序在 Flash & HTML5 上运行;
  • 2013 年 11 月:第一个 App 在 iOS 商店通过审批;
  • 2014 年 4 月:第一个 App 在 iAndroid PlayStore 通过审批;
  • 2015 年 11 月:Flash 退役,Flow 在浏览器中完全迁移到 HTML5;
  • 2016 年 1 月: Google Material 指南 实施;
  • 2016 年 11 月:增加 64 位 JIT;
  • 2018 年 5 月:使用 Flow 本身编写自托管的编译器;
  • 2019 年 4 月:发布第一个开源版本。

在这 9 年中,Flow 逐渐发展成为一个成熟可靠的平台。Flow 中的代码被数百万用户使用,并在现实生活中得到了验证。

有那么多的东西可以用,为什么 Flow 很有价值?

主要原因是 Flow 是一个稳定、成熟的平台,它不仅提供了编程语言、增量编译器、多后端,而且还提供了一个功能强大的标准库,包括一个一流的 UI 库。

Flow 语法非常简单,易于学习、读写,并且基于函数语言家族中的清晰语义,使用了大多数程序员都熟悉的 C 风格语法。

它是一种实用的语言,具有可以利用类型推断的静态类型,但在需要时也支持动态类型。它提供了开箱即用的安全性和防故障能力。它鼓励函数式代码,但在需要时也支持可能带来副作用的引用。

因此,就其本身而言,Flow 是一种很好的语言,可以与其他语言相抗衡,简单易用,并且在与其它编程语言更为具体的比较中,支持简单的代码构建跨平台 UI 是最大亮点。

UX 是最大的特性

Flow 之所以重要,主要是因为 UI 的标准库 Material。这是我们多年来设计和实现的这一类别的库的第四代,当你编写跨平台的应用程序,如果需要在浏览器、平板电脑和手机上使用键盘、鼠标和触屏时,它会成为真正重要的东西。

设计一个适合自己和一小群程序员的 UI 工具包是一回事,而设计和改进一个允许数百名程序员一起快速安全地生成一个连贯而漂亮的用户界面的工具包则比较困难。在没有大量图形设计人员和 UI/UX 专家来详细设计所有内容的情况下,会更难。

因此,在我们的经验中,Flow 实现所需的代码比 HTML+CSS+JS 中的代码少 2~4 倍,甚至比使用 C#、Swift 或 Java 的标准框架所需的代码还要少。更重要的是,开发人员的初始实现通常也会非常好,无需多次迭代就能达到生产质量。

Flow 作为一种函数式语言也意味着 UI 代码是高度可组合的,促进了大量重用。简而言之,这意味着构建应用程序的生产率非常高,而且不必在用户体验的质量上做出妥协。

Flow vs HTML5

JavaScript 框架非常丰富,功能非常强大,是当今最流行的编写应用程序的技术,不仅可以在线使用,还可以作为 Progressive Web App 在设备上使用。

基于 HTML 的技术依赖于浏览器非常强大的功能,但也面临框架频繁更改的挑战,也许现在是最流行的框架,但每隔两年就会有新的东西出现。Flow 多年来一直很稳定,并且已经证明自己有能力与最好的框架竞争。如果你学会了 Flow,那你可能就不需要频繁学习其它新东西。

Flow 还有另一个优势,举例来说, React 教程 中曾介绍了一个井字游戏示例,实现以后可以和另一个人进行比赛。在 React 的实现中使用了 112 行 JavaScript、38 行 HTML 和 51 行 CSS,总共 200 行。

而同样的游戏使用 Flow 只需要 83 行。

值得注意的是,在 Flow 中,你不必将代码分割为三种不同的语言。这使得学习和编写都变得更容易。完全相同的 Flow 代码可以作为 iOS 和 Android 的原生应用程序运行,并且外观和行为都是一样的。

因此,UI 实现的总体复杂性在 Flow 中更低,因为它只涉及一种简单的语言,所以更容易学习。根据我们的经验,它还可以更好地扩展到具有许多复杂交互的、漂亮的、响应性、自适应 UI。由于所有代码都是用一种语言编写的,因此更容易进行推理和重用。最重要的是,它可以像本地应用程序一样运行。

如果我们比较这两种语言本身,JavaScript 和 TypeScript 的语义相对复杂,而 Flow 是一种简单而强大的语言。

Flow vs 多范式语言

许多语言,如 Java、C#、C++ 和 Swift 都是多范式语言,它们同时支持 OO、函数式和命令式范式。它们提供很棒的库,被数百万人用来编写我们每天都在使用的应用程序。

如果我们将 Flow 与这些语言进行比较,最大的区别是 Flow 代码更短,更容易理解。原因有以下几点:

  • 轻量级的、强大的基于表达式的语法。Flow 的语法有 255 行,包括注释,总共 25 个关键词。
  • 类型系统很简单,但是仍然支持类型推断、子类型和动态类型。抽象数据类型允许通过编译器的强类型检查对数据进行自然建模。
  • 一致的标准库。因为 Flow 是一种函数式语言,所以标准库就是围绕这个范式设计的。特别是,我们大量使用函数式反应式编程来处理交互性。这意味着各个部分可以很好地组合在一起,即使对于 UI 代码,也具有很高的组合性。
  • 代码通常很容易理解,部分原因是没有混合多种范式及其交互。没有重载、没有隐式转换、空指针或方法重载。当你阅读代码时,可以确切地看到它做了什么,因为语法和语义是一致的,并且不随上下文而改变。
  • Flow 将 HTML 视为一级目标。上述语言不能自然地在浏览器中运行。其中一些提供了实验性的 WebAssembly 目标,但是这些目标需要比较大的运行时,因此需要大量的下载。令人惊讶的是,它们的运行速度比 JS 慢很多倍,而且 UI 工具包没有移植到浏览器中。因此,使用这些语言不容易编写包含浏览器在内的多平台代码。

除了最后一个优势外,其它优势都是和函数式编程语言共享的,比如 ML 或 Haskell。那么,与函数式语言相比,Flow 怎么样呢?

Flow vs 其他函数式语言

Flow 完全属于 ML 语言家族。它是饿汉式、强类型、具有类型推断,支持可能带来副作用的引用。因此,它最接近 ML、OCaml、F# 等类似的语言。

可以说,它们之间最大的区别在于语法。Flow 被有意设计成使用带有花括号的 C 语言家族语法,这对于大多数来自 JavaScript、Java、C# 和类似语言的程序员来说更容易学习。但是,与这些语言相比,Flow 仍然是一种基于表达式的语言,因此,在简单性和表达性之间保持了很好的平衡。

另一个不同之处是 Flow 故意避免了 ML 中我们认为初学者很难学习和使用的部分。Flow 的函数需要括号,如 f(1,2) 与 f 1 2,这种做法有助于消除是哪个函数的参数的歧。

函数在 Flow 中是一等公民,并且有未命名的 Lambda 表达式,但是 Flow 不支持“局部套用(currying)”。局部套用是一种非常强大的技术,通常被认为是函数式范式的关键部分。那么,为什么 Flow 不支持呢?关键原因是,作为一个初学函数式编程的人,局部套用很难理解,并且会使代码更难阅读。你有时会对符号序列的类型和含义产生疑问。所以,我们一直没有支持局部套用,局部套用使用得不够频繁,它需要一个语法上的快捷方式,与编写一个只在局部生效的短短的 Lambda 表达式相比,它会使理解变得复杂。

ML 及类似语言的一个主要优点是强模式匹配。Flow 支持简单的模式匹配,但不支持嵌套模式或守护模式。这主要是因为 Flow 希望保持语言简单。在 Flow 中,基本的 Flow 编译器是自托管的,模式匹配肯定有助于缩短一些代码。但在编译器之外,我们的经验是它并不常用。也就是说,添加这一个特性的拉请求将受到欢迎。

一般来说,Flow 代码通常比 ML 代码稍微长一些,尤其是 Haskell,这在一定程度上是有意为之,因为我们认为过于简洁的代码可能会让非专家人员难以理解。

出于同样的原因,Flow 的类型系统是一个相对简单的 Hindley-Milner,没有 Monad 或类型类。它支持子类型和动态类型。这似乎提供了一个安全性和表达性的最佳结合点。在编写复合 UI 代码时,子类型帮助很大。UI 组件在很多方面都有细微的差别,因此,子类型有助于使 API 接口之间相互更加独立,更易于理解和学习,同时仍然具有强类型的优点。

简而言之,如果你喜欢函数式语言,并且想要编写具有漂亮 UI 的应用,Flow 是一个不错的选择。它很简单,经过验证,带有一个成熟的、功能强大的 UI 库,它可以借助一个很小的运行时在 Web 上运行,也可以作为原生应用在移动设备上运行。

发现 Flow 与其它语言之间的差异

我们还可以将 Flow 与脚本语言(如 Python)、Lisps、Prolog 和其它语言进行比较。Flow 的好处主要体现在之前说的地方,只是会有些微差异。如果你想要知道详细的差异,那么可以亲自去 GitHub 上体验一下 Flow

查看英文原文: Flow. Why, oh why – one more language?