功能性的未来。与Oscar Spencer合作的谷物

109 阅读37分钟

在本月的 "Functional Futures "节目中,我们的嘉宾是Oscar Spencer--一种名为Grain的函数式编程语言的共同作者,该语言可编译为WebAssembly。

听一听这期节目,了解更多关于Grain、WebAssembly以及如何向开发者推销函数式编程。

你可以在我们的YouTube频道上查看完整的情节,或者在我们的播客页面上听音频版本

下面,你可以看到这一集的一些亮点,为了清晰起见,进行了编辑。

我们与Oscar Spencer谈话的要点

学习WASM

**Jonn:**你好,Oscar,我的介绍公平吗?

**奥斯卡:**你好!是的,我认为你的介绍很中肯,这几乎就是我的概况--只是整天坐在那里做WebAssembly--整天,每一天。

**Jonn。**对,而且你已经做了一段时间了,对吗?你很早以前就开始做Grain了,那时好像是2017年,对吗?

**奥斯卡:**是的,2017年。

**乔恩:**所以你当时已经是一个WASM大师了,决定要做一个语言,还是怎么做的?

**奥斯卡。**不,我不是一个真正的WASM大师,我想除了Mozilla的人之外,当时没有太多的人是WASM大师。我想那是在WebAssembly刚刚被所有主要浏览器默认开启的时候,而我在那时是一个巨大的前端人员。所以我对这个想法感到非常兴奋。"嘿,等等,我们可以在浏览器中运行非JavaScript的语言吗?我绝对想试一试"。

当时,我实际上刚刚完成了一门编译器课程,我想如果我和我的一个朋友一起做一些针对WebAssembly的事情,会非常有趣。然后,你知道,我们做到了--我永远不会忘记当我们成功地从WebAssembly内部绑定一个DOM事件时,我们脸上的震惊,那绝对是惊人的。我想我们都兴奋地从椅子上跳了起来。"嘿,我们点击了按钮,WebAssembly就发生了!"

所以,那真的很令人兴奋,想到那是在2017年,这很疯狂。在某种程度上,我觉得我是和WebAssembly一起长大的,所以我一直都在这里,边走边学,我是说,这很好。

**Jonn。**现在很多人在Grain工作时,基本上都或多或少处于你五年前的位置。你是如何学习WASM虚拟机的概念以及它是如何执行的?你只是读了20遍说明书,然后就明白了吗?或者你的学习过程是怎样的?

**Oscar。**有趣的是,我们有很多的帮助。Grain编译器最初的目标是X86。我不想说它无聊,但对于这些天来说,它可能有点无聊。因此,我们在网上发布了官方的WebAssembly规范,每个人都可以阅读。然而,有一个用OCaml编写的WebAssembly的实际参考实现。

这对我们来说有点像作弊代码,因为我们不需要花太多时间就能搞清楚。我们所做的只是从OPAM下载这个WASM库,然后我们把它作为我们的目标。我们就像这样。"嘿,我们要把这些指令中的大部分换成WebAssembly的。"做像寄存器分配这样的事情是非常容易的,因为在WebAssembly中我们有无限的寄存器,所以它实际上不是太糟糕的开始,特别是如果你是一个像我们这样的OCaml书呆子,现在仍然是。我们有一个相当容易的时间。所以,是的,在开始的时候肯定有一点作弊的成分。

但在那之后,它应该只是,你知道,阅读规范,与人们聊天。有这么多可爱的人,他们很乐意帮助你了解社区里发生的任何事情。你只要伸出手来打个招呼,人们就会非常乐意帮助你。

**乔恩:**对于一个初学者来说,要问关于WASM及其特殊性的问题,应该去什么地方?

**Oscar。**有一个官方的WebAssembly Discord,人们可以加入,他们在那里谈论所有的WebAssembly,你可能想象不到人们正在用WebAssembly做的事情--人们在那里谈论它。

我认为加入这些圈子是很好的,但也要加入你想使用的实际语言和框架的特定圈子。在这些圈子里,你可以得到更多的具体信息。对于 "我如何在Rust中做这个确切的事情?"或 "我如何在Grain中做这个确切的事情?"人们会有更好的答案。

人们会给你很好的答案,因为就像你说的,WebAssembly仍然很新。要弄清楚这一点并不容易,例如,一旦你设法制作了一个WebAssembly模块,你如何真正去部署它并在生产中运行它,对吗?有一些工具可以让你更容易,也有一些人在Kubernetes中运行WebAssembly,让它变得非常疯狂,但你现在要了解这些东西的方式是通过个人与人们的聊天,主要是在不同的讨论区。

我们将达到这样的地步:我们已经有了所有的工具,而且超级简单,所有的东西都有很好的记录,但作为一个社区,我们仍然没有达到这个地步。但我们正在迅速达到这个目标。

谷物

**Jonn。**你能告诉我们一些你对Grain的看法吗?如果我必须为Grain做一个电梯演讲,我会说它正好是WASM的Golang。这是个公平的比喻吗?

**奥斯卡:**是的,在某种程度上是。我认为我对Grain的长期愿景是成为人们进入WebAssembly的简单入口,并在后台有一个非常狡猾的计划,让函数式编程接管未来。

整个要点是让这种语言非常容易接近。我认为,这是我在研究React框架、ReasonML和类似的语言时真正掌握的一个概念。你可以采用一些相当 "先进的功能概念",但如果你以一种让人们说 "嘿,实际上,是的,这很有意义,我的代码中没有错误 "的方式来展示它们,当你能够讲述Messenger.com如何在转向ReasonML后从一天10个错误报告变成一年10个错误报告的故事时,当你能够讲述这些故事时,这让人们非常兴奋。因此,我们正试图用Grain做很多这样的事情。我们可以给你所有这些类型的安全,我们可以给你所有这些功能特性,你将能够写出真正好的生产代码,但它并不可怕,它是可接近的。

我们在Grain中打破了一些规则。比如,我们有let mut ,我们有默认的可变绑定,这可能会吓到一些人,但我们希望有这些地方,人们可以进来,并真正感觉到他们是在家里,感觉到他们在写一种他们非常舒服的语言,而且他们真的感觉不错。我的意思是,在这个过程中,我们会教他们一些功能概念,[......]让人们接触这些东西,让他们对这些东西感到兴奋。

因此,这也是我们对Grain的期望--提供一个坚实的开发者体验,让人们在编写任何他们想要的程序时感觉非常好。基本上是90%。所以,你想写的90%的程序,你可以用Grain来写,而另外10%的WebAssembly你需要写,你可以用Rust写。这就是我们的目标,即我们想成为多高层次的语言。

**Jonn:**在这里要澄清一点。我也看到了很多关于Grain和Elm的比较。我对Elm的个人经验是,它大概是80-20,但20的东西真的是边缘的不可能。就像,你必须通过这样的圈套才能得到那20%的用例,而80%的用例并不包括在内,这几乎让我个人怀疑我是否应该首先去做这件事。你是如何为那些知道自己在做什么并且真正需要这个逃生舱的人创造逃生舱的?

**奥斯卡。**这是我们一直在考虑的问题。我们想确保人们能够在Grain中做一些我们不一定赞同的事情,所以我们有几个功能来做这个。例如,其中一个是你可以放在函数上的一个属性,叫做不安全,它是一个允许你说的属性。"嘿,我现在就要在这里写低级别的WebAssembly。"这不是我想让99%的用户去做的事情,然而,我们要确保它是可能的。首先,这让我们的事情变得更容易一些。Grain的整个运行时间都是用Grain编写的,如果不能够编写一些裸露的WebAssembly的东西,我们可能就无法做到这一点。但它在语言中提供了这样的逃生口,人们可以做一些事情,特别是当它涉及到,例如,绑定到WebAssembly主机。现在,这样做--这是一些低级别的代码,我不希望每个用户都这样做,我们正在研究像[听不清]这样的项目,允许你自动生成Grain代码来绑定这些接口,所以你不必做这样的事情。

但是,我们想确保我们在Grain中拥有这些东西,所以人们可以去他们想去的地方。我还要补充一下。关于WebAssembly的一个大的令人兴奋的事情是WebAssembly组件模型,我们希望能够混合语言,我们希望能够从所有地方拉入组件。

所以当然,也许你不想用Grain写密码学库,但没关系,你可以把Rust惊人的高性能密码学库拉进来,对它非常满意,并把它集成到Grain代码中。我们现在就能做到吗?没有,但我们确实可以达到这个目标。

我告诉你,如果有人来到Grain的聊天室,说 "嘿,我想静态链接到这个Rust二进制文件",我会说 "好吧,让我们实现它",因为如果人们现在要求的话,我们确实可以实现它。但这绝对是WebAssembly未来的发展方向,我们会有这些不同的方法让你得到最后10%的东西,你可能不想用Grain来写。

**Jonn:**这真是太酷了,非常非常高兴听到这个消息!你提到了与主机平台的绑定。你提到与主机平台的绑定。据我所知,这些是由主机提供的能力,例如,文件系统将是一个这样的绑定或像浏览器中的localStorage

**Oscar:**是的,没错。WebAssembly代码在沙盒中运行,所以它是完全封闭的,完全没有办法与外界对话。这就是WebAssembly如何在默认情况下获得安全。

我们开始添加新功能的方式(因为也许你确实想和文件对话,或者你确实想和网络对话,或者其他什么)是我们通过主机函数添加这些功能。有一组主机函数用于常见的系统调用--WASI,即WebAssembly系统接口--它提供了一种标准的方式来与系统上所有这些不同的系统调用进行交互。所以我们有这个功能,但是,此外,你可以让你的主机提供任何你想要的主机功能,来做各种各样的事情。

例如,有几个游戏引擎给你一些主机功能,说 "这是控制器的输入 "或 "这是一个缓冲区,你可以为图形写入"。这是非常酷的,而且会有各种不同的主机提供各种不同的主机功能。

即使是在Suborbital,我们也希望人们能够做一些事情,比如非常容易地与数据库对话,当然,我们可以让你建立一个网络连接,做所有这些有趣的事情,或者我们可以只给你一个函数,说 "嘿,我想查询这个东西",然后从那里走。所以,这就是主机功能的意义所在。当然,人们需要能够绑定到这些主机函数,但它们是非常低级的东西。如果你曾经在一种语言中写过C语言的绑定,那正是这样的--你在写那些与这些主机函数看起来像什么的绑定。就像我之前说的,我们正在努力使很多事情自动化,所以人们不必自己去做。

我认为我对Grain的长期愿景是成为人们进入WebAssembly的简单入口,并在后台有一个非常狡猾的计划,让函数式编程接管未来。

Grain编译器

**Jonn:**我简单地看了一下 Grain 仓库,我看到编译器是用前面提到的 ReasonML 写的。你知道,我并不经常看到用ReasonML编写的代码,尤其是源自2017年的代码。能否请你先谈谈选择的语言,其次,你如何让ReasonML真正发出WASM,因为据我所知,它有点像双目标语言,它的目标是JavaScript,我想它的目标是x86。

**奥斯卡。**有趣的是,早在2017年,Grain的编译器就是用纯OCaml写的。OCaml的原因是--你知道,那个ML部分代表着 "元语言"--它是编写语言的完美语言。我会把这句话带到我的坟墓里,没有人可以告诉我其他的。用OCaml开发编译器绝对是一个梦想。如果你想告诉我,OCaml对于所有这些其他的用例来说是不好的,随便你,但对于构建一个编译器来说,OCaml是非常棒的。

我不太清楚它是什么时候发生的,我想也许是2019年或2020年,当我们切入到ReasonML时,搞笑的原因是那里没有很多的OCaml开发者。他们肯定存在,我们在这里,我们是一个社区,但没有那么多的人。在这一点上,ReasonML已经变得更流行了,可能比OCaml更流行一些。这是一个相当容易的转换--我们可以运行一个工具将所有的OCaml转换为ReasonML,突然间,我们有了一个完整的社区,这些人可能有兴趣来为编译器做贡献,你知道,开源是很难的,我们需要尽可能多的帮助。这就是我们在ReasonML的起源。这只是为了拥有仍然光荣的OCaml编程来编写我们的语言。

对于你的另一个观点,即我们如何让ReasonML发出WebAssembly--实际上并不是ReasonML编译器在做这个。Grain编译器最终只是一个普通的二进制文件,但实际的WebAssembly发射是通过一个叫做Binaryen的项目进行的。Binaryen是一个项目,本质上是给你一个以WebAssembly指令为目标的IR,然后它可以为你串行化和反串行化模块。它非常方便,当然,在这之上,它有大量的优化功能。

我们本可以在我们的后端使用完整的LLVM,你知道,Binaryen也是LLVM背后的项目,但我们不需要这样。我们真的知道,"嘿,我们的目标是WebAssembly,让我们跳过所有LLVM的部分,让我们直接使用Binaryen",因为我们仍然得到所有WebAssembly的优化,如果我们通过LLVM,我们会得到这些优化。不是LLVM IR的优化--那可能是相当强大的,但我们可以处理,那是我们可以自己处理的事情。

但是,我们为Binaryen写了一些OCaml绑定,我们就是用它来生成WebAssembly。

**Jonn:**你是如何确保这些优化对你的运行时间有意义的,你在使用Binaryen的过程中是否需要做任何调整?

**Oscar。**澄清一下,我们确实有一整套的优化措施,作为我们运行的Grain编译器的一部分。你可以把这些优化看作是让Binaryen做更多的优化。所以它主要是说。"好吧,我们可能有一些代码,在WebAssembly层面上,Binaryen不一定了解如何优化,但它是我们完全了解如何优化的东西,我们可以照顾它。"

因此,我们有一堆我们自己运行的程序,然后在那之后,我们还可以有那些Binaryen的优化。这是对我们的代码进行调整,以确保我们推出的WebAssembly模块是容易优化的。

**Jonn。**从我目前对WASM的了解来看,WASM本身并没有定义一个运行时间。这取决于WASM规范的用户,他们要弄清楚他们究竟想如何运行这个字节码。所以,对我来说,这是在智力上变得有点棘手的地方,你说的运行时是什么意思?

**Oscar:**是的,这是个棘手的问题。当我说Grain的运行时间时,我具体指的是两件事。第一件事是内存分配和垃圾收集--我们如何实际管理所有这些内存,以及我们对所有这些正在分配的对象所做的一切。这是第一件大事。而这一点是非常重要的。

有一个关于垃圾收集的WebAssembly提案正在进行中,我们对它感到非常兴奋。我认为这将是让不同语言的模块连接在一起的一个重要部分。这是你现在最关心的问题:你可以有两个网站模块做一些事情,但要让它们互相交流,它们就会在对方的内存上写。

Grain运行时的第二部分是所有支持事物的基础设施:比如print 功能,比如toString 功能,诸如此类的东西。有点像你期望存在于语言中的语言支持的东西,但这些代码必须实际存在于某个地方,它不只是免费提供给我们的。

**Jonn:**在Grain方面,我知道你自己做了运行时间。你能谈一下它的属性吗:它是严格的,还是懒惰的,你的垃圾收集策略是什么,等等。

**Oscar:**所以,关于WebAssembly的一件事是--有些人觉得它有点烦人,有些人觉得它很好--你写WebAssembly的方式就像它在一个堆栈机器上运行一样。所以,你把值推到堆栈上,你把值从堆栈中弹出,你有一个好时机。这一切都很好。但是WebAssembly不允许你做的一件事是检查你的堆栈。所以没有办法看到堆栈里有哪些值,哪些值是活的,所以我是否可以走走我的堆栈,做一些垃圾收集,这不是你今天用WebAssembly可以做到的。

人们解决这个问题的方法是他们实现了自己的堆栈,他们只是让实际的网站代码操纵内存中的堆栈。这也是一种方法。对我们来说,我们选择使用WebAssembly的堆栈,这样就摆脱了我们可能会做的一整套垃圾收集。所以我们改用引用计数垃圾收集。我们只是跟踪你对这个东西有多少个引用,如果它死了,我们就回收这些内存。

这就是所发生的一切,相当简单。但它仍然是一个垃圾收集器,这意味着维护起来很麻烦,所以我们一直在仰望WebAssembly规范之神,要求 "请提供sgc"。当我们不再需要维护这个时,我们会非常感激。没有什么比GC的错误更伤害我的灵魂了,只是在调试上损失了很多时间。

在语言属性方面,我们是相当严格的,当你写的时候,我们会评估东西,它在那里被评估,你不必担心它是否会被偷懒,这是否会在以后发生。我想,这可能会让一些人感到失望,他们可能希望看到到处都有懒惰的评估,但是,是的,不,我们是相当严格的。

OCaml的原因是--你知道,ML部分代表着 "元语言"--它是编写语言的完美语言。我会把这句话带到我的坟墓里,没有人可以告诉我其他的。用OCaml开发编译器绝对是一个梦想。如果你想告诉我,OCaml对于所有这些其他的使用情况来说是不好的,随便你,但是对于构建一个编译器来说,OCaml是非常棒的。

紧跟WASM规范的步伐

**Jonn:**这实际上让我想到了下面这个问题。在整个采访过程中,你说了很多类似于 "从今天起,WASM有这个属性 "的短语。你是如何应对规格的变化的,因为我假设你想一直站在最前面?

**奥斯卡:**我想这是我们现在最大的挑战之一。有一些我们支持的和我们想支持的运行系统,在保持他们的WebAssembly内部引擎的更新方面可能有点落后于时代,实际上能够运行一些这样的东西。

这是一个问题,因为我们想继续快速发展,我们想使用所有最新的功能和其他东西。因此,我们有一些编译器标志来处理这个问题,关闭特定类别的指令。例如,我们有一个标志是 "没有大量内存"。这就好比是,不要使用大容量内存指令,然后去找一些这些东西的polyfills。这有点让人难过。我们确实想走在事物的最前沿。

我想说的是,很多人都很擅长更新他们的运行时。我们可能最终会有更多的标志来开启和关闭功能,但到了年底,我想我们可能会变得更严格,我可能会开始放弃旧的WebAssembly功能。因为,是的,WebAssembly正在迅速发展,我们看到了提案的落地,事情正在发生。

我认为一个真正的大问题是尾部调用,例如。我们等了很久才让WebAssembly支持尾部调用,现在我们做到了。如果有人在Grain上开了一个问题说。"嘿,我写了一个尾部递归函数,但它炸毁了堆栈,"我至少不会高兴,因为这是WebAssembly中存在的一个功能,但不是所有的运行时都支持它,所以,默认情况下,我们没有打开这个标志。而且你必须知道在某些运行时打开这个标志。

所以,我认为这有点像我们不会支持旧的功能,我们会期望人们--你知道,一年前就被标记为完成的提案,我认为我们可以有把握地说:"嘿,是的,如果你想继续使用这种语言,你需要去升级你的引擎。"

但是,说实话,我认为一些旧版本的Grain也很不错,就像他们可能会在一个版本上停留一段时间,但我认为这没什么。

**Jonn:**就你个人喜欢或你的团队而言,确切地说:你提到有一些功能正在为WASM实现,但是否有突破性的变化,你是否需要做大量的重构来适应这些新功能或突破性的变化?

**Oscar:**我不得不说,WebAssembly团队在使WebAssembly处于一个不需要有一系列破坏性变化的位置上做了了不起的工作。二进制格式--主要版本自发布以来没有改变,我们仍然在WebAssembly的第一个版本上,所以除了已经引入的新指令外,没有任何随机字节会让运行时直接崩溃。

因此,一些运行时,当他们试图加载一个模块时,可能会说 "哦,我不认识这个操作码,我会死的。"你知道,这没关系,它发生了。但就所有准备WebAssembly的工具而言,你知道,所有这些东西都在继续工作。

因此,我们没有遇到太多的麻烦,就像 "唉,他们又来了,破坏了东西。"实际上,这已经很令人愉快。如果我没记错的话,现在有一个关于WebAssembly特性检测的提议,以确保 "嘿,我知道这个运行时将支持这些东西",你可以根据可用的特性来编译不同版本的模块。当我们可以做这样的事情时,那将是相当不错的。只要我们能够支持几个标志,把功能打开或关闭,那么你知道我们会好起来的。

我不得不说,WebAssembly团队在使WebAssembly处于一个不需要有一系列突破性变化的位置上做了了不起的工作。二进制格式--主要版本自推出以来没有改变,我们仍然在WebAssembly的第一个版本上,所以除了已经引入的新指令外,没有任何随机字节会直接使运行时崩溃。

重构谷粒

**Jonn:**就你的早期工作而言,如果我们回溯一下历史,你是否从第一次尝试就击中了要害?

**Oscar。**哦,绝对没有。[笑]

**Jonn:**那么,在重构编译器代码或你写的运行时的位子等方面,你的经验是什么?

**Oscar。**那次经历有点像从一个玩具语言到像一个真正的、实际的、严肃的语言的旅程。因为,是的,一开始代码库只是一个学校项目的结果,我不知道你怎么想,但我不是真的想把代码从一个学校项目运送到生产中,对吗?因此,我进行了大量的重构,从头开始重建了很多部分,使它们成为生产级的。

从那时起,我作为一个工程师成长了一吨,团队中的其他人作为一般的工程师也成长了一吨,但也是编译器工程师,这就是理解更多的概念和我们在开发时应该做的事情。

我想很多都是以这种方式重构代码,重构我们在语言中的很多想法,只是我们想如何推理事物,真正的打击,"好吧,我们到底想让语言去哪里?"因为在开始的时候,我们不一定知道,我们只是有点好玩,我们看到了一个机会--如果我们付出一点点努力,一点点努力,我们实际上可以建立一个人们会喜欢使用的东西,人们实际上会想要使用。

因为,从根本上说,我们现在的很多WebAssembly语言都是超级低级的,这对一些写WebAssembly的人来说是很好的,他们可以处理这些。但绝大多数人可能想写一堆高级代码。因此,我们要考虑如何使Grain更适合这种情况,如何使Grain成为一个让人们写代码和运行代码都非常容易接近的地方,并且运行得相当快。它是否会像一个非常严肃的Rust模块那样快?也许不是,但考虑到WebAssembly的速度,大多数人可能不需要它。

所以,它只是重新修改了很多语言的想法,当然,也有很多代码。但是,在通往1.0的道路上,有许多我们想要完成的功能和事情。人们一直在要求的一个巨大的功能是宏。我们想在语言中使用宏。这是一个巨大的、多毛的功能,希望我们能有机会尽快开始工作。

OCaml / ReasonML

**Jonn。**OCaml或ReasonML,现在,是否帮助你更好地制定你的想法,或者你做白板头脑风暴,然后把你的想法塞进类型系统中?它是如何进行的?

**Oscar:**肯定是两者都有。但我不得不说,我的思维是由OCaml塑造的,这有点可笑。我认为OCaml为我做的主要事情是它真的让我首先考虑数据。这与很多其他语言非常不同,在其他语言中,你可能会考虑一个模型,你可能会考虑一个对象和你可以对这个对象做的事情,但我喜欢OCaml的一点(这一点肯定已经开始进入Grain)是先考虑数据。

这意味着你首先考虑的是类型。你首先考虑的是,好吧,我如何对这些数据进行建模,这就像是第一步,然后我们开始考虑如何转换这些数据,如何移动这些数据,如何对其进行处理。

我认为这也是Grain体验的很多工作方式。我们有一个特定的目标,不是 "嘿,让我们开始写代码,希望我们能做到这一点"。不是,而是 "这就是我们要实现的目标",然后从那里开始建立自己。

我记得在学校的时候,我们有一个叫做设计秘诀的东西,基本上就是考虑你的数据,考虑你的变换,甚至考虑你的测试。[这也是我们今天所做的很多事情,我们首先要考虑的是真正的问题是什么,从那里开始建模,然后最后一步是 "好的,现在我们要实现它"。

我肯定受这种想法的影响很大。

**Jonn:**这真的很酷,而且现在这些方法也能通过类型进入前端,这很好。

**Oscar。**绝对的。

**Jonn。**对于重构,我认为类型系统有明显的好处。举个例子,当你在一个地方改变了一个东西,然后编译器会告诉你你错过了什么。

**Oscar。**哦,是的,这就是Grain编译器的整个开发过程。这真的很有趣,特别是当我向人们解释Grain编译器的代码库时,我总是告诉他们,这实际上非常简单,我们只是从IR到IR。所以,从本质上讲,你只需在最后的编译步骤中,对代码进行修改,然后说,好吧,我想让这个数据输出这个WebAssembly代码。然后,编译器亲切地引导你一路回到编译器的每个阶段,确保你实现了,如何lex这个,如何解析这个,如何去抽象法线形式,我们如何做所有这些不同的事情。

这真的很酷,这就是我希望每个人都有的开发经验:做一个小的改变,然后不要尝试和猜测,让我们不要让工程师到处走动,就像。"哦,我应该改变那个文件中的这个东西吗?"不,让我们有一个工具,可以告诉我如何做。这很重要。

有趣的是,我们在Grain中的一些功能,人们认为是革命性的。比如,通过模式匹配,你忘记了一个案例,编译器会告诉你 "嘿,你错过了一个边缘案例,顺便说一下,这里有一个你错过的代码的例子。"从未见过这种情况的人--这让他们大吃一惊--他们就像 "哇,这太疯狂了",就像 "这真的是你避免错误的方法",而你就像 "是的,这很疯狂,这一直都存在,只是在主流语言中没有,但是是的,它在这里,你可以避免所有种类的错误。"

这也是我最喜欢像OCaml这样的语言的原因之一--你写了一些代码,如果它被编译了,那就是正确的。它可能不会做你想做的事,但它绝对是正确的,是你写的东西。这对我来说是惊人的,我认为这有点可爱,对,当你关注所有这些编译器错误时,通常,当你真正让程序实际编译时,它通常 "在第一次尝试中 "工作。这并不是你真正的第一次尝试,因为你运行了12次编译器命令来达到这一点,但是当你达到这一点时,你通常已经完成了,你可以松一口气,说 "啊,我实现了这个功能",然后提交你的PR。

因为你不得不使用这些美丽的类型和思考的东西,所以PR的评论是如此可爱。它更多的只是一些想法,比如 "哦,我们可以这样来表达这个类型 "或者 "我们可以这样来考虑这个问题",它没有那些低级别的--我多么讨厌JavaScript中的那些讨论,只是喜欢。"哦,实际上,在这个函数中,你有这种边缘情况,当它是空的而不是零时......"。我讨厌这些东西,所以取消这类讨论真是太棒了。

**乔恩。**你在说服人们相信这一点上有什么问题吗?我经常听到的一个论点是,非类型化语言或单类型化语言就像是用于快速原型开发的语言,而你不能像ML-like语言或Rust那样快速前进,或类似的东西。你是否有这种反对意见,如果有,那么你是如何应对的?

**奥斯卡:**我认为很多,而且我一直在向团队宣扬这一点:Grain的最大问题是我们如何讲述我们的故事。这是所有关于我们如何谈论语言的问题。这就是为什么你不会看到我到处谈论你如何在Grain中做这个非常复杂的功能的事情,你永远不会听到我说这些事情。所有这些都是关于 "我如何能够更快地开发","我如何能够建立更好的程序"。 我认为当你带着这种心态来的时候,很多人就会更愿意接受它。

因为当你告诉人们他们必须以某种方式编写程序时,他们会立即变得非常防卫并说 "哦,但我不想这样做"。例如,在类型问题上--我认为这是一个惊人的问题--在Grain中,所有类型都是推断出来的。所以我可以告诉别人--特别是像JavaScript或TypeScript的开发者,他们会说 "哦,我真的很喜欢TypeScript,但我得写所有这些类型"--实际上,想象一下,当你写代码时,编译器完全知道你在想什么。

比如,当你在那个变量上写下加号时,编译器知道那个东西是一个数字,这意味着这个参数是一个数字,这意味着,在你使用这个的所有这些函数中,这个东西是一个数字,而且编译器会确保你对它的一致性。

然后,有人可能会对你说"哦,但是类型不在代码中,我看不到类型,所以我怎么知道这些东西是什么类型?"但是现在,拥有惊人的编辑器支持是巨大的。有了它,我们不仅有像VSCode那样的,你有你的代码镜头,每条语句都会告诉你。"嘿,这是这个东西的类型",你可以悬停在任何数值上,它会告诉你这是什么。

你减轻了很多这些顾虑,你告诉人们,嘿,你可以写出完全相同的代码,你甚至不必考虑类型,你可以像在Python中那样写代码。你只需写代码,不用考虑它,运行它,你就完成了。不同的是,编译器会让你知道,在我们的例子中,嘿,你做的事情有点不对,只要确保你澄清这一点或修正这一点,这样的话,不仅对其他阅读代码的人来说是明显的,对你和编译器来说也是如此。

然后,要让人们接受这一点其实是很容易的。当你告诉他们这是个超级神奇的东西,它能确保你永远不犯错,他们会非常兴奋。当你告诉他们,你可以从你的代码中完全消除空指针异常,这是你永远不需要担心的事情,他们会因此而兴奋。

因此,我认为这完全是关于我们如何谈论这些事情,以及我们如何让人们思考这些问题。因为,是的,那么你就会得到更少的反击。我认为很多人,当他们来到Grain时,他们已经看到,就像,"哦,是的,这很好",就像,"我可以写这个代码,我感觉非常舒服,我可以使用这个库,这不是一个大问题"。然后,当然,这门语言慢慢地、偷偷地开始在你身上添加更多的功能概念,但这没关系,你张开双臂,接受写程序的感觉有多好,你对你的程序有多大的信心,他们在做正确的事情。

这真的很酷,这就是我希望每个人都有的开发经验:做一个小的改变,然后不要试图猜测,让我们不要让工程师走来走去,就像。"哦,我应该改变那个文件中的这个东西吗?"不,让我们有一个工具,可以告诉我如何做。这一点很重要。

来自观众的问题

**乔恩。**好的,我们有一个辣手的问题。你认为对于函数式程序员来说,现在开始使用WASM的最佳切入点是什么?我应该直接学习Rust吗?

**Oscar。**真够辣的。好吧,你可以从几个不同的角度来看这个问题。我认为,Rust有足够的功能特性,对于普通的功能型人才来说,他们会很高兴。你有强大的借贷检查器,它将告诉你如何生活,你有模式匹配,各种各样的东西,所以如果你只是想做一些函数式编程,你完全可以学习Rust。

我认为这真的取决于你想写代码的水平。如果你想写超级低级别的代码,是的,继续学习Rust,但如果你想找一些更简单、更友好的东西,是的,我想应该是学习Grain。

在函数式编程语言方面,现在这些将是你真正的好选择。然而,有一个叫Asterius的Haskell WebAssembly编译器,所以如果你特别是一个Haskell人,你想要Haskell,你也可以去看看那个项目,你可能会很高兴,因为它正是Haskell。

但是,是的,我不打算告诉人们不要写Rust。Rust是一门了不起的语言,它是一门神奇的怪异语言,即使在工作中,当我能写它时,我也很兴奋。但在一天结束时,它是相当低级的。Grain就像那个更高层次的Rust朋友。我想有人在Twitter上把Grain描述为JavaScript和Rust的孩子。我有点同意这一点,我认为很多人也可以这样想,你知道,进入一些功能性的东西,有足够的你想要的功能。

有些人只是得到一个结果类型,他们很高兴,这就是他们所关心的,但是,就像,这有不同的层次。

**Jonn:**我想我们还有最后一个问题。"Oscar说,WebAssembly有80%是在服务器端使用的。这有点令人惊讶。我们能不能给服务器端的WebAssembly做一个简短的电梯推介?"

Oscar:[笑]哦,你不应该问我这个问题。是的,你可以在服务器上用WebAssembly做很多事情。所以,我认为我们之前谈论的事情,关于WebAssembly是那种一次写完就可以到处运行的语言。不是要完全打乱JVM,但在某种程度上,这就是它现在正在做的事情。

有一个叫WAMR的WebAssembly运行时--WebAssembly微运行时--它可以在任何东西上运行。如果你想在任天堂DS上运行WebAssembly,你完全可以,它就像那种水平。因此,你实际上可以把你的WebAssembly代码放到所有这些地方。

所以,有了WebAssembly的沙箱功能和其他类似的东西,突然间,我们以前无法真正拥有的用例突然变得众所周知。你可以安全地运行来自你不认识的人的任意代码,这有点疯狂。这开辟了大量的机会。

因此,我们在Suborbital所做的是,我们扩展你的SaaS应用程序,无论你有什么样的应用程序,都可以让你的用户写函数,在你的应用程序中做事情。以前,这可能是你不得不运行一些定制的Docker容器解决方案,这可能仍然有点不安全,但现在我们实际上可以完全锁定沙盒,并说:"不,你可以做的事,我不知道。"不,你被允许做这件事,而且只能做这件事,就这样。"

能够这样做实际上是一种巨大的。有很多像这样的用例,然后甚至进入取代Docker容器的阶段。"嘿,实际上我们不需要这些大而笨重的容器来做什么。如果我可以有一个WebAssembly模块来完成这个任务,我就可以把所有这些模块连接起来,完成所有的工作。我都准备好了。"

这就是我们如何在服务器上进行大量的WebAssembly的原因。甚至在边缘计算方面也是如此。这就好比,我怎样才能真正把计算推给我的用户?将大量的二进制文件和容器运送到世界各地是非常麻烦的,但当我有一个10万的WebAssembly模块时,实际上我很容易就能做到:"好吧,把这个模块放在印度的服务器上并运行它。"这实际上就像是更容易考虑的事情。它实际上是非常严肃的。我想这就是让我们对WebAssembly的能力感到兴奋的原因。

就像,是的,WebAssembly将在浏览器中流行,不要误会我的意思,我们会达到这个目的。我想现在很多人忘记的是,从根本上说,JavaScript是一种好语言。即使它不是人们所希望的那样,但它已经做了不寻常的事情,人们已经用JavaScript做了不寻常的事情。JavaScript的速度真的很快。有些人用Rust写程序,就像这样。"我要用Rust写这个东西,然后部署在浏览器上。"然后他们发现,这并不比JavaScript版本快多少。

这是因为JavaScript引擎很疯狂,它们的JIT代码速度比你想象的还要快。这真的是非常非常令人印象深刻。所以,在浏览器之外,在很小的地方运行,在边缘的服务器上运行--我们看到有很多WebAssembly的应用。我仍然梦想着有一天,一些Grain代码会在某个地方的灯泡上运行。我想,你知道,我们一定会达到这个目标。


非常感谢Oscar作为Functional Futures的嘉宾。🙏

要想听到他的更多信息,你可以在Twitter上关注Grain语言。此外,你可以在GitHub上查看Grain,或者加入他们的discord

如果你想听到更多我们的声音,请务必在你喜欢的播客平台上订阅Functional Futures播客。