许式伟:Go+ 演进之路

7 月 10 日,一年一度的 ECUG Con 2022 在线上圆满举行。许式伟作为七牛云 CEO、ECUG 社区发起人、Go+ 语言发明人,为大家来带了《Go+ 演进之路》的主题演讲。以下内容根据演讲实录整理。 

图片

 

大家好,欢迎来到 ECUG Con 2022。ECUG 大会从 2007 年开始,到今天已经第 15 个年头了,我基本每年都会为大家带来演讲。继上届大会之后,今年我想和大家继续分享 Go+ 的相关内容,聊聊 Go+ 的演进之路。我们会谈谈 Go+ 过去都发生了什么?我们现在正在做什么?以及我们未来会怎样继续去进行迭代?

 

图片

 

一、Go+ 历史的关键节点

 

纵观 Go+ 的发展历程,我们大概会分四个关键的节点。

 

图片

 

首先是 v0.5 版本及以前的「史前版本」。因为它当时叫做 qlang,其实和 Go+ 没有关系,所以叫「史前版本」。我们现在把 qlang 的代码,从 Go+ 移到我个人的 GitHub 下面了。之后是 v0.6 到 v0.7 的原型版本,主要是为了让大家看到 Go+ 到底长什么样,因为它和之前的  qlang 有非常大的不同,qlang 是一个脚本语言,Go+ 实际上是一个静态类型的语言。此后,我们从这个原型版本出发,让它能够更加接近工程的使用。比较重要的里程碑就是去年 v1.0 版本的发布,Go+ 的目标和代码风格被正式确定下来。此后,我们基本上是延续这个目标和它的代码风格继续前进。今年上半年我们发布了 Go+ 的 1.1 版本,它实际上是 Go+ 的第一个工程化版本,它可以正式用于生产环境。

 

图片

 

从 Go+ 1.0 开始,我们首先提出了「三位一体」的概念,即面向工程,STEM 教育和数据科学。

 

实际上我们谈的是全民编程,也就是人人都可以学编程。在今天我们可以看到,编程教育在未来,一定有越来越多的人会把它看作基础学科,和数学、语文、英语没有什么本质区别。这也是为什么 Go+ 会把 STEM 教育作为非常重要的支撑点。

 

那么 Go+ 1.0 都做到了什么呢?首先是确定了 Go+ 的代码风格,它是以命令行风格为基础,极尽可能去实现低门槛化我们希望 7 到 8 岁的小朋友,就有能力学 Go+。

 

另外一个很重要的点,是我们实现了类文件 Beta 版它实际上试图实现面向对象、领域知识表达的低门槛化也就是现在比较火的低代码领域。实际上面向对象虽然是好的东西,有助于对世界的抽象,但是它也带来了理解上的难度。因此如何去让这些高阶的工程概念低门槛化,Go+ 类文件是在这方面最重要的探索。

 

另外 Go+ 1.0 在兼容 Go 语法方面取得了突破性的进展,这也是它最后被标为 1.0 的原因。我们在这个版本上,把大部分 Go 语法都实现了比较好的兼容性,基本上做到了在 Go 基础上去做扩展这样一个最底线的目标。

 

谈到 Go+ 的目标,大家可能会有非常多的疑问,实际上从刚才的目标也可以看出,我们非常关注低门槛化。那谈到低门槛化,就不得不提 Python 这个语言。Python 的成功,到底告诉人们什么事情呢?

 

图片

 

首先第一个重要的点在于,它告诉我们性能并不是最重要的。虽然大家都比较看重性能,但单从性能来看的话,Python 在脚本语言里面我认为只能算二流它其实并不快因为性能其实是可以靠时间去解决的。语言的生命周期都非常长,Python 到今年已经有 32 年的历史了,它的性能问题是有机会可以靠时间来不断迭代解决的。但语言的特性不能每一步语言特性的选择都是未来的包袱。所以从这个视角来看,也希望大家对待语言,尽量避免唯性能论吧。

 

第二个点在于,它揭示了对语言来说最重要的是什么?或者说 Python 为什么能成功?其实我觉得,目标人群的选择是非常根因的东西。语言的特性跟目标人群的选择有关,所以语言特性的选择最重要。Python 从诞生之初,并没有给自己数据科学语言的定位,它认为应该让语言尽量精简,容易被理解、被学习。它其实是一个少有的低门槛语言,因为在我看来,真正可以称为低门槛的语言并不多。

 

我们想一下大家熟知的语言,比如说 Ruby,大家都说它很简洁,但是其实它有非常多的语言魔法。所以它可能很强大,但是不能称之为易学习。所以在我心目中,能称得上是低门槛的语言 BASIC 算一个,面向教学领域的 Scratch 算一个,也就是说其实在低门槛领域进行探索的语言并没有那么多,但正因为 Python 面向了低门槛,所以它虽然没有将自己定位成数据科学语言,却成了数据科学的王者。这其实蛮讽刺的,因为有非常多专注于数据科学的语言都没有 Python 这么成功,我认为这背后有非常深刻的道理。

 

从全民编程这个大的趋势来说,其实低门槛化是未来语言主流的发展趋势,Python 恰恰顺应了这个大潮流,使得它今天能够比最初我们看到的还要成功。

 

那么 Python 到底还差什么?

 

图片

 

首先从工程的视角来说,Python 这方面比较弱。谈 Python 有些人会想到 Go,因为从 Python 转向 Go 的程序员也不少,原因就在于 Go 是设计思想最接近 Python 的工程语言,使用者的心智负担是非常低的。但是 Go 语言的设计者心中基本只有工程,大家看 Go 的官网就知道,它最关心的一个词是 scale。也就是如何实现一个大型的工程,实现一个代码量非常庞大,但是仍然在工程师掌控之中的工程。所以它没有在意低门槛,它在意的是如何去实现工程上的 scale。

 

所以从这两方面去看, Go+ 既然是面向全民的编程,那么我们自然会去关注如何把 Go 和 Python 的优势融在一体,把 Go 的工程能力和 Python 的低门槛化结合。

 

我们知道工程很庞大,去实现更庞大、更复杂的系统也是我们所关注的,但是 STEM 教育也好,数据科学也好,更关注的还是如何实现低门槛化。所以工程、STEM 教育、数据科学三位一体,实际是工程和低门槛的融合,这就是 Go+ 的目标。

 

图片

 

它们融合之后的样貌,我们可以通过下面 Go+ 的代码范例来了解。

 

下面的这个示例大家可能会想到 Shell 编程,很多程序员可能会觉得 Shell 脚本是比较低门槛的。它虽然在实现复杂任务上比较难用,但是它从理解上是大家最熟悉的,我们会看到 Go+ 的语法与 Shell 的非常接近。

 

图片

 

 

我们再看另外一个例子,就是用 Go+ 去做游戏。下图实际上是两个角色的对话,一个非常简单的游戏,它的代码也是非常简洁的。这里我们会看到命令行的影子,比如 onStart 这样的语句,是说在程序开始的时候,我们应该做什么。onMsg 是我收到一个消息以后做什么基本上有 onStart 和 onMsg 这样的事件机制,以及我们看到里面的代码有 say、有 broadcast(广播消息)。整个程序的流程,其实是通过事件加上 say 还有广播消息,这样几个很基础的元素组成。

 

图片

 

我们可以看到这是两个角色的对话第一个角色在程序开始的时候,说你来自哪里,然后紧接着广播消息 1。另一个角色收到了消息 1 后,他就会说我来自英国。然后他再广播消息 2。角色一收到消息 2 以后,他就会说你们国家的天气怎么样。这样整个时序就由消息驱动,两个角色之间的对话就形成了。这个程序非常简洁,通过它我们可以看到 Go+ 在表达自然的语义上,有天然优势,它代码是非常通俗易懂的。

 

从这两个例子中我们可以看到,Go+ 虽然实际上是 Go 兼容的产物,但它的语法或者说建议的最佳实践风格,和 Go 是有非常大差异的。它甚至比 Python 还要简洁,因为它选择了命令行的风格。

 

这里我们可以从工程的几个概念来理解。一是命令,它是一段代码的抽象化,最早期的语言如 FORTRAN,它的命令其实是和函数分开的。当然后来所有的高级语言,基本上都把命令和函数合为一体。但在 Go+ 里,命令和函数代码风格上来说是有差别的,但是它们背后都是函数。

 

图片

 

所以在 Go+ 的代码风格上,我们选择了以命令风格为主体。因为命令的理解难度是最低的,小学生就能理解。其次是函数,这个概念初中生基本也就开始接触,比如三角函数。那结合计算机和数学中的函数,将两者互相印证,对初中生而言理解起来难度也不算高。基本上只要理解形参和实参的概念就可以了。

 

在面向对象中,类、方法这些概念,虽然它确实有助于抽象世界,但实际上面向对象编程的理解门槛是最高的。所以 Go+ 其实在极力避免让程序员用面向对象的写法。实际上背后我们会使用面向对象的一些思想,但在语言语法上,我们尽量避免太过于面向对象化。所以 Go+ 1.0 中我们看到了它基本奠定了 Go+ 的代码风格和目标。

 

图片

 

既然目标和风格都定了,基本上我们希望的第一件事情,就是能够实现工程,成为第一个可以用于实际生产环境的版本。为了实现这个目标,从优先级来说最重要的有两件事情。一个是对模块 Module 的支持,大家都知道 Go 对 Module 支持是很晚的, Go+ 在 1.1 版本基本上兼容了 Go 的模块概念。我们实现了对模块比较完备的支持,和 Go 用起来的体验是非常一致的。另外一个很重要的特性我们实现了 Go 和 Go+ 的混合工程。这对用于生产环境是有非常大的帮助的。因为大部分程序员面临的第一个问题,就是 Go+ 用来做什么?历史的工程可能是 Go 写的,那如何把它转化成 Go+ 呢?其实不用转,因为你的 Go 工程,就是 Go+ 的工程,你只需要在上面写一些 Go+ 的函数就行了。这样一来,我们就可以非常轻松地去把 Go+ 用于生产环境。

 

接下来是提供 c2go 的预览,这是为后续版本服务的,是一个非常难啃的骨头。我们也在 Go+ 1.1 版本基本把它实现了。Go+ 支持 C,实际上是引用了 c2go 这个项目。我们在这个版本实现了 c2go 最基础的能力。

 

图片

 

我们看 Go+ 的版本演进如果说 Go+ 1.0 版本是明目标、定风格,那 1.1 版本是为了进生产环境。模块也好,Go/Go+ 混合编程也好,其实都是在为进入生产环境打基础。

 

图片

 

Go/Go+ 的混合编程,正如我刚才提到的,任何一个 Go 工程,只要在其中添加几个 Go+ 的源代码,然后去把 go 的命令换成 gop,就可以正常地去做 Go+ 开发了。这实际上把 Go+ 的使门槛降到了最低。

 

二、Go+ 当前节点:v1.2.x

 

图片

 

以上是 Go+ 的过去,下面我想和大家分享当前 Go+ 正在做的事情,也就是 Go+ v1.2  版本。这个版本我是把它定义为 Go+ 特色化形成的过程。我们预计在今年十二月份将 Go+ v1.2 版本正式发布。

 

我们说这个版本是特色化形成的过程,主要有这样几个原因。首先是我们在 1.0 版本中引入的类文件会转正,结束 Beta 过程。类文件是 Go+ 里非常重要的概念。第二个是 c2go,它对 Go+ 后续发展起着至关重要的作用。我们希望 Go+ 的 v1.2 版本能够让 c2go 进入工程化,它的实现标志至少完成了 sqlite3 的迁移。

 

了这两个很特色的功能Go/Go+ 混合编程也得到增强目前,Go/Go+ 的混合编程还不支持调用 Go 的泛型。我们知道 Go 的 v1.18 版本,引入了非常重要的特性就是泛型,但现在 Go+ 还不支持 Go 的泛型。那我们在 v1.2 版本也会去支持。我们不是在 Go+ 里去定义泛型,而是用 Go 的泛型。

 

基于这样方式,我们是让 Go+ 能够对泛型概念有最小化的能力,因为泛型是一个比较复杂的概念,但我们并不希望 Go+ 变得特别复杂。从长远来看,Go+ 对泛型实际上持非常开放的态度,也许有一天会全面支持泛型,但我们不会把它看成优先级高的东西。如果需要用泛型,我们希望通过和 Go 的混合工程来达到。

 

我们接下来重点讲讲这两个特色功能,类文件和 c2go。

 

先聊类文件类文件最直白的一个解释,就是我们用一个文件去定义一个类。下图右侧就是用 Go 去写类的方法,想必大家非常熟悉。

 

图片

 

我们先定义一个叫 Rect 的结构体,它有长度和宽度两个成员,那我们再定义面积的成员方法,那就是长度和高度的乘积,这就是一个非常简单的程序。

 

用类文件来实现这个能力的话,代码可以见上图左侧。我们基本上看不到任何面向对象的隐藏。我们定义了两个全局变量,一个叫宽度,一个叫高度,然后定义一个全局的方法叫面积,它是这两个全局变量的乘积。那这个代码比正常的面向对象代码看起来要简洁很多,非常容易被理解。但实际上它这两个文件是等价的。因为类文件最直接的能力,就是负责把一个看起来像是面向过程的代码,自动变成一个面向对象的方法。因为它没有引入任何新的语法,所以其实对中小学生也相对容易,不用去学新的知识。

 

图片

 

但类文件做的并不只这些。实际上它还能够自定义基类,能够自定义整个程序执行的框架。我们最近 Go+ 的公众号也重点在谈类文件,大家可以去看一看。

 

图片

 

提到类文件,就需要提到 Go+ 的一个设计哲学:Go+ 不支持领域专用语言,也就不是不支持 DSL,但 Go+ 却对专业领域友好(Specific Domain Friendly)。

 

为什么它是专业领域友好呢?从上文我们的举例中来看,第一个例子是 Shell 编程,或者叫 DevOps 的领域编程。我们可以看到 Go+ 的代码看起来非常接近于 Shell 编程。

 

图片

 

实际上 Shell 编程更像是 DevOps 的 DSL,我们很少在 Shell 之外去用这个语言,它只用于非常基础的一些自动化(automation)。但是我们可以看到,Go+ 实际上是可以让语言本身的代码非常接近于 DSL实际上它却不是 DSL,它是非常正宗的 Go+ 语法。

 

同样的道理,我们可以看到在复杂的游戏编程中,Go+ 也是非常的简洁的而且它不仅简洁,更重要的是它同时也非常强大,它能够去做类似于《植物大战僵尸》这样比较复杂的游戏。当然我们没有去做 3D 游戏的引擎,如果要做,那么它依然是非常精简的 3D 游戏的引擎。

 

图片

 

是因为 Go+ 引入了类文件,让它的语法看起来非常领域化。这意味着 Go+ 非常善于结合领域的特征去提炼领域知识。从而使得 Go+ 利于领域的开发

 

当然我们类文件现在还在 Beta 的阶段,当前已经在 2D 的游戏、DevOps 这些领域做了一些实践。但是毕竟领域是非常多的,对类文件这个概念来说,最大的挑战是领域非常多,在有限的几个领域里试验是不够的,需要有更多的领域来验证类文件机制的普适性,判断它能否适应各个领域。

 

图片

 

另外,我们需要清除 Beta 版本类文件里面的一些不必要的约束。比如说我们当前一个专业的领域只允许有一工作类,在一些专业领域里显然有可能会需要打破这个约束。所以我们接下来在 v1.2 版本,会去消除掉类似这样的不必要约束我们希望能够让一个专业领域有多工作类,如此一来它的普适性就更强。

 

v1.2 版本的第二个特色功能,就是刚才提到 c2go。c2go 的语法可能看起来有点像 cgo,但是它和 cgo 完全不可同日而语,cgo 用起来大家吐槽非常多,但是用 c2go 会觉得非常爽,因为我们基本做到了无缝对接 C 语言。

 

图片

 

首先,C 语言的代码是不需要经过额外包装的,直接就可以由 Go+ 来调用。其次,我们让 C 和 Go+ 的类型系统尽可能一致,极大化地降低了 C 和 Go+ 的对接成本。这样的话两者相互操作,类型上基本不用做转化。比如说在 C 语言里面的 void,就是无返回值、无参数的一个函数,到了 Go+ 里,基本上是一个 func()。那这样一个类型的映射,其实在 cgo 里面是做不到的。因为 cgo 需要做必要的函数调用约定转化,才能实现这样的调用。最后,我们把 C 翻译之后,它的数据结构内存布局和程序语义尽可能保持不变。也就是说 C 程序员,对 C 的代码的常规语义理解仍然是正确的。比如说字符串,它是以 '\x00',就是以 0 为结尾的,这些概念到了 Go+ 这边翻译以后,其实它仍然是正确的。这样也会有助大家不至于在语义上出现分歧。

 

从下图的例子中我们可以看出,通过 c2go 的方式,我们实现了 C 的简洁调用。

 

图片

 

我们可以看到第一句是 import C,但是它和 Go 的语义是完全不同的,在 Go+ 里它其实是 C/github.com/goplus/libc 的缩写。那我们可以看出,这个例子中我们调用了 2 个 C 函数:printf 和 fprintf,使用了一个 C 变量 stderr。另外还有一个比较有意思的地方,是字符串,我们看到在 Go+ 的标准字符串前面写一个 C 前缀,就代表 Go+ 里传入 C 的字符串常量,这实际上是一个 Go+ 的语法。但是有了这个语法以后,会使得在 Go+ 里调用 C 的代码会非常精简,不至于像 cgo 一样,会有一个从 Go 字符串转化成 C 的字符串,并且最后还要释放它这样一个过程。

 

这是一个非常小的例子,但是我们可以看到 c2go 在表达上,是能够让大家感觉到好像 C 和 Go 的包是没有区别的。我们引入 C 的包和引入 Go 的包,基本使用上大差不差。而且在所有的细节上,都会让大家感觉 C 的模块好像就是 Go 的模块,当然同时也是 Go+ 的模块,这是我们希望能够达到的最终效果。这也是我们在 Go+ 里无缝兼容 C 的一个逻辑。

 

当然当前 c2go 还是一个预览版,它连 Beta 版都算不上。c2go 当前已经完成了 C 语法 99% 以上的兼容,它没有完成的部分,主要在于标准 C 库的迁移,它的完成度可能只有 5%,处在非常早期的阶段。对 c2go 来说,它最主要挑战首先在于跨平台。一方面是 C 标准库(libc)的跨平台,另一方面如何让 c2go 对所有的 C 工程都可以轻松实现跨平台能力,同样非常重要的能力建设工作。

 

图片

 

从 libc 本身来说,其实无论是 syscall、pthread,都有比较大的工作量。syscall 现在我们已经支持了 mac 版本,但 Linux 和 Windows 还没有支持,那 pthread 就更不用说了,这是我们接下来工作量最大的一个板块。所以 c2go 接下来最重要的是整个标准 C 库的迁移,它本身就是比较庞大的工作。

 

以上就是当前 Go+ v1.2 版本想要解决的事情,当然还有支持 Go 模板调用的小细节,我们就不展开了。基本以上几个点构成了 Go+ 的特色能力,无论是类文件,还是对 C 的兼容,以及 Go 和 Go+ 的混合工程,都使得 Go+ 有了非常好的底子。

 

三、Go+ 未来规划

 

那么从 Go+ 的未来规划来说,大家都知道 Go+ 在谈工程和 STEM 教育、数据一体化,实际上到 v1.2 为止Go+ 在数据科学领域做的事情是相对少的会有一些非常有限的能力去实现比如列表解析、range 表达式等等,但谈不上体系化,实际上 Go+ 的数据科学技术栈还是没有的。

 

所以在 v1.7 这个非常重要的大版本里面,我们希望 Go+ 自身数据科学的技术栈能够形成。然后到 v2.0 又做了一个大的版本越级,我们希望 v2.0 这个阶段,要能够支持 Python 语法。当然,不是说在 Go+ 里面去写 Python。实际上跟支持 C 比较类似,能够让 Go+ 无缝地去 import Python 的包,这样就使得 Python 数据科学领域历史的积累,都可以无缝变成 Go+ 的数据科学能力。

 

图片

 

实际上这两个内容,都是面向数据科学的。原因在于,从工程和低门槛化的大方向来说,到 v1.2 版本基本上能力已经完备。Go+ 基本上不太会去在语法上花很多精力,这一点 Go+ 和 Go 是有非常相似的哲学,我们认为语法越少越好,而不是越多越好。所以,基本上到了 v1.2 版本以后,Go+ 的语法比较定型了,我们不太会加各种稀奇古怪的语法。

 

但是数据科学是 Go+ 最后的攻坚战,它不是简单的一个语法创新上能够解决的问题。Go 的数据科学基础能力比较薄,当然也是因为 Go 本身兴起的时间比较短,所以它在服务端的工程实践居多。

 

数据科学底子这么薄的话,应该怎么办呢?v1.2 版本把 c2go 能力去工程化以后,为最后的数据科学攻坚战打下了重要基础。因为 Python 的基础是 C,我们如果对 C 做好了兼容,兼容 Python 就变得更加简单了。

 

图片

 

v1.7 版本我们会关注什么?它的目标实际上是 Go+ 数据科学技术栈的形成,打造 Go+ 自身的数据科学能力,比如向量、矩阵等一些方面的探索。实际上即使有了这样的基础能力,它仍然还是比较单薄的。

 

图片

 

如何真正解决这个问题,让自己站在巨人的肩膀上?我们的设想是通过 c2go 来支持 Python 的数据科学底座(也就是 C 库那部分),v1.7 版本我们希望能够把 Python 的 C 库部分进行兼容,从而走上 Go+ 和 Python 数据科学能力生态融合的道路。但是这个阶段我们对 Python 本身不会去做太多考虑,主要还是关注 Python 的 C 库部分。

 

图片

 

但是到了 v2.0 版本,我们就开始考虑把 Python 的语法也引入进来,让 Python 的包自然地成为 Go+ 生态的一部分。我们设想在 v2.0 版本,至少要支持 CPython、NumPy、pandas 这三个工程。第一个是 Python 本身,其余两个是最知名的 Python 数据科学工程,有了它们以后,我们认为 Go+ 就具备了对数据科学的基础生态能力。

 

这里为大家展示这三个工程的代码,列一下代码行的结构。我们看 Python 本身,大部分底座的代码是 C,但是有 65% 的 Python 的代码其实主要是一些标准库,这个是很容易理解的。但是最核心的能力都是 C 完成的,占 30% 多。

 

图片

 

第二个就是 NumPy,NumPy 其实也是一样的做法,它最核心的能力就是 C 写的,还有少量的 C++。但是在这个基础上迭加了一个工具包,是用 Python 自己写的。从这个代码行可以看到,占比基本在 6:3、6:4 的样子。

 

图片

 

pandas 结构有非常大的差异,它本身大部分代码是 Python 写的,C 的部分比较少。基本上到了 v2.0 版本才能比较好地支持 pandas,v1.7 版本基本上还不太能支持 pandas,但已经可以支持 NumPy 了。

 

图片

 

总结一下 Go+ 的演进之路,我们的目标是实现工程、STEM 教育、数据科学的三位一体。这主要还是因为我们未来的语言,主流趋势是面向全民编程,也就是人人都可以学编程。实际上这个目标很难,但它是未来语言发展的主流趋势。目前鲜有语言在面向这样的趋势努力,Go+ 可以认为是第一个。

 

图片

 

今年内,Go+ 在工程化和低门槛融合的探索就将告一段落。从明年开始,我们将对数据科学发起最后的攻坚战。大家都知道,Go+ 诞生之初我们就在谈数据科学。但是实际真正去执行的时候,数据科学反而放到最后一点。

 

原因在于数据科学真的是比较难的一件事情,对 Go 来说它的距离比较远。但是我们基本上有了 c2go 的基础,就会发现它对我们日后去做好数据科学,会产生很重要的支撑作用。

 

最后,我相信有了数据科学的支撑,Go+ 会是独一无二的。它的未来,值得我们共同期待。

 

 
posted @ 2022-07-15 10:49  七牛云  阅读(641)  评论(0编辑  收藏  举报