Motoko:一种直接在Internet上构建的编程语言

2020-08-28 15:31:18

在Dfinity,我们正在建设互联网计算机,这是一个分散的云计算平台,我们将其设想为一个无缝的软件世界,开发人员可以在其中直接在互联网上部署应用程序和服务。为了实现这一愿景,我们决定将WebAssembly作为平台执行环境的通用语言,以便开发人员可以用编译成WebAssembly的任何语言对其进行编程。

为了提供无缝的开发人员体验,我们还认为创建一种称为Motoko的专用编程语言非常重要,该语言旨在直接支持Internet计算机的编程模型,从而更容易高效地构建应用程序并利用此平台的一些不寻常的功能。

我们很高兴能与您分享这个项目的特别之处。

首先,我们必须简要地谈一下WebAssembly-也就是。WASM(是的,拼写正确,不全部大写)。您可能知道,Wasm是一种较新的低级代码格式,其目标是可移植、安全和高效。它最初的用例是Web,但这个名称实际上用词不当:当我们在W3C工作组中设计Wasm时,我们小心翼翼地将其作为一个开放标准和通用平台。也就是说,它不针对任何特定的编程语言、范例、计算环境或平台,并且我们确保它完全不与Web捆绑在一起。因此,Wasm在云计算、边缘计算、移动、嵌入式系统、物联网和区块链等许多其他环境中采用Wasm绝对不是偶然的。

在WASM中有很多很多设计方面的考虑,有些显而易见,有些则相当微妙。太多了,这里进不去。关于Wasm的技术目标、设计选择、形式语义和实现技术的相当全面的讨论可以在我们发表在“ACM的通信”上的一篇科学文章中找到(本文的一个更老的、更技术性的版本可以在这里免费获取)。

与其他虚拟机相比,WASM的主要不同之处在于,它没有针对任何特定的编程语言进行优化,而只是抽象了底层硬件,其字节码直接对应于现代CPU的指令和内存模型。最重要的是,Wasm通过强大的模块性和严格的数学规范来支持沙箱,确保执行是安全的,没有未定义的行为,并且(几乎)完全是确定性的。而且,这些性质实际上有机器验证的数学证明!

总之,这些特性旨在使Wasm成为对便携性、安全性、通用性和性能有很高要求的各种环境和用例(如Dfinity的互联网计算机)的有吸引力的选择。

互联网计算机是一个分散的云计算平台,它将托管安全软件和一种新型的开放互联网服务。它使用强大的密码共识协议在对等网络(可能不受信任的)计算节点上安全地复制计算,该网络可能覆盖有许多虚拟子网(有时称为碎片)。WASM的优势属性使其成为表示在该平台上运行的程序的明显选择。我们还喜欢这样的想法,即不将开发人员限制为只使用一种专用的平台语言,而是将其潜在地向“所有人”开放。

不管怎么说,这就是理论。在实践中,将现有编程语言移植到Wasm并非易事。显然,它需要实现一个新的编译器后端。这很有趣,但努力并没有到此为止:它还需要移植语言的运行时系统和库原语。还有一些特性,特别是与更高级的语言相关的特性,目前在Wasm中还不能很容易地实现-例如:线程、协程、异常和尾部调用。虽然用各自的功能丰富WASM的各种提议即将出现,但它们还没有最终确定为标准化。

尽管已经有许多针对Wasm的实验性语言实现,但大多数还没有准备好进入黄金时间。其中包括低级系统语言,如C/C++和Rust。对于它们的用例来说,这些当然是很棒的,但是它们不是为Internet计算机开发高级应用程序的理想工具,在Internet计算机中,可访问性、生产力和高保证往往比手动干预内存管理更可取。

在包括Internet计算机在内的一些平台上,要运行Wasm还需要克服额外的障碍,这些障碍与它们提供的计算环境的限制有关。例如,Dfinity的Internet计算机与传统操作系统几乎没有相似之处:没有文件、I/O或其他功能,这些功能通常在语言实现中被视为理所当然,并在运行时或库中自由使用。这意味着移植现有语言不仅仅是调整代码的问题:您可能需要找到新的方法来替换缺少的平台功能,删除它们,或者做出完全不同的设计选择。像WASI这样的努力试图在一定程度上解决这个问题,但仍处于初级阶段。

不可避免的是,这些因素使得Dfinity的互联网计算机的语言移植工作变得非常重要,即使在采用已经存在通用Wasm端口的语言的情况下也是如此。

同时,互联网计算机的语言需要提供对平台主要概念的访问:具有异步消息传递的分布式编程模型,资源的概念,如循环(也称为。天然气),以及其他一些特质。当然,它们都可以作为库提供,但是本机包含适当结构的语言可以提供更加无缝的编程体验。

因此,如果我们无论如何都要工作才能起步,为什么不致力于创造一些能够提供最佳用户体验的东西,并传达我们对如何对互联网计算机进行编程的愿景呢?

这就是为什么--尽管存在创造另一种语言的所有风险--我们决定创建Motoko。我们想要一种安全、易于使用、无缝地公开平台概念的语言,以及一种对大多数程序员来说看起来足够友好和可访问的语言。目前,后一个目标使得它几乎不可避免地牢牢地处于语言的分号和花括号阵营。而且这个营地里没有合适的语言。

但Motoko相当传统的皮肤只是表面上的:它的内部是一种现代语言的内部。例如,每个构造都是一个表达式,它有闭包,它有不同的类型和静态检查的模式匹配,它有垃圾回收,当然它还有一个灵活的类型系统,它实际上是健全的,也就是说,它确实保证了某些错误的存在,比如崩溃、未定义的行为、曲解数据,或者只是在开关中遗漏了一个大小写。没有洞!

与此同时,我们有意尝试不花哨或重新发明轮子,而是建立在丰富的实践和理论历史的基础上,并承认几十年来在这一领域学到的教训。除了将人们熟知的功能组合在一起之外,Motoko的设计还结合了许多小决策,以最大限度地减少步枪和安全方面的错误,例如,缺省情况下数字不能溢出,缺省情况下局部变量是不可变的,缺省情况下并发执行是原子的,缺省情况下不会出现null,缺省情况下字段是私有的,等等。哦,而且没有继承,只有子类型。

实现Motoko的这些部分并将它们编译成Wasm是传统的编译器工艺。用OCaml编写的Motoko编译器使用类型化的中间表示法、几次转换,并输出Wasm字节码。生成的Wasm模块包括一个用C和Rust编写的小型运行时系统,该系统主要实现一个使用Wasm内存作为堆的简单垃圾收集器。这并不难,但这里肯定有很大的改进潜力。

然而,Motoko的中心特性是它在语法和类型系统中对参与者的直接支持。演员模型是一个有40多年历史的著名概念,但遗憾的是,它几乎没有进入主流语言。参与者类似于对象(在Motoko中,甚至看起来也像对象),因为它封装了私有状态以及一组处理可以发送给它的消息的方法。但是所有的消息发送都是异步的。因此,与面向对象中的传统方法不同,参与者方法没有结果。此外,所有消息都由参与者按顺序接收-也就是说,它有一个隐式消息队列,即使在并发发送消息时,方法也会自动执行。

参与者是并发编程的一个很好的模型,因为它们自动防止争用条件(由于原子性和封装状态)和死锁(因为执行从不阻塞),因此排除了许多并发错误。所有这些都不需要程序员定义锁。参与者也是分布式编程的一个很好的模型,因为异步自然地处理向潜在的远程接收者发送消息所涉及的延迟。最后,参与者非常适合Dfinity的互联网计算机,在这种计算机中,应用程序以所谓的容器的形式部署-本质上,参与者被表示为可以跨子网络通信的Wasm模块。事实证明,Wasm的模块概念非常适合这一点,因为我们可以将模块导出直接解释为参与者方法。因此,Motoko参与者将编译为Wasm模块,其中的方法成为具有平台定义的特殊参数约定的导出Wasm函数。

简而言之,Motoko中的应用程序是一个(或多个)参与者,而参与者又是一个编译成Wasm模块的大型异步对象。使用Wasm的内存概念,这样的参与者可以立即管理高达4GiB的内部状态,尽管可以通过链接多个每个都有自己内存的Wasm模块来进一步扩大这一范围。我们很好奇第一批用户会以多快的速度达到这个内存限制。

为了使异步编程更方便,并允许以顺序的“直接风格”表达它,Motoko采用了编程语言研究历史上另一个40多年前的想法:期货(在一些社区中也称为承诺),不过幸运的是,这个想法最近变得更流行了一些。在Motoko中,它们以“Async Values”的形式具体化,这是由前缀为“Async`”关键字的表达式生成的`Async<;T>;`类型的值。具体地说,函数体可以是异步表达式,从而自然而然地取代了存在于某些其他语言中的更为单一的“异步函数”概念。

如此一来,参与者方法终究可以有结果-只要这些结果是未来的。可以等待期货获得它们的值,但只能在另一个异步表达式中进行,类似于其他现代语言中已知的异步/等待单数。

Motoko编译器通过传统的CPS(延续传递样式)转换来实现这一点,将每个等待点转换成一个单独的Wasm函数(加上一些闭包环境)来表示等待的继续。实际上,它是双桶CP,因为每条消息也可以有一个故障回复,其中包含各自的故障继续。按照约定,具有异步结果的方法是发送携带结果值作为参数的回复消息的方法。此消息由创建的延续函数接收,然后该函数可以恢复其捕获的执行。等待回复不会阻止参与者-它可以在此期间自由接收其他消息。

Motoko的另一个重要考虑是允许开发人员在不必学习全新类型的计算的情况下利用区块链技术。因此,我们拿出了您在当前类型的区块链编程语言方面可能需要的所有专门知识。例如,没有可观察到的块或块高度的概念,没有用于更新区块链上的状态的显式构造,也没有用于将数据写入持久存储的其他API,例如文件或数据库(尽管可以将其模拟为库)。取而代之的是,互联网计算机实现了正交持久化,这是另一个古老的想法,在这种想法中,程序有一种“永远”运行的错觉,它的内存保持活力(至少在它被显式删除之前是这样)。在Motoko中,这意味着开发人员不必担心显式保存他们的数据,也不必担心文件或外部数据库:当下一条消息到达时,程序变量中存储的任何值或数据结构都将仍然存在,即使是在几个月后。

该平台负责在方法调用之间透明地保存和恢复容器的私有状态。在Wasm引擎上进行改造相对容易,因为Wasm模块的状态明显隔离在模块的内存、全局变量和表中。在大多数情况下,使用操作系统公开的虚拟内存技术查看Wasm内存就足够了。这样,平台知道这样的存储器中的页面何时被修改,并且可以采取任何必要的措施来持久化脏页,以及为分布式共识协议散列它们。

因为互联网计算机运行的是Wasm,所以Motoko只是创建应用程序的一种选择--而且是有意这样做的。我们期待着提供其他语言选择。即使这样,因为每种语言都将统一编译成Wasm中表示的容器,所以这些容器可以通过消息发送自由地相互通信,而不管它们的源语言是什么。

为了使这种互操作性定义良好,我们还引入了一种名为CANTID的通用接口定义语言(IDL),它独立于Motoko。它描述了容器能够理解的一组消息,以及随其发送的数据类型。通过独立于Motoko类型系统或任何其他编程语言的规范数据类型(数字、文本、数组、记录、变量、函数、对其他容器的引用)的组合来坦率地描述数据。

哎哟,又是另一种类型的系统吗?嗯,程序员可能会很高兴,因为Motoko编译器可以为参与者导出和导入自动消费和生成这样的接口描述,并将它们从相应的Motoko类型映射到相应的Motoko类型。它还自动生成正确的Wasm代码来序列化和反序列化每条消息的参数数据,透明地将Motoko的内部表示与CANTID指定的二进制格式相互转换。

这样,Motoko程序就可以以一种类型化的方式与外部容器通信,并将远程调用表示为程序中的本地对象。无论远程容器是用Motoko还是用Rust编写的;容器的接口描述作为类型信息就足够了。除了方便之外,接口还提供了一种强大的模块性形式,其中程序可以针对其他参与者/容器进行类型检查,而无需访问其实现。

我们的目标是,互联网计算机将成为一个多语言平台,所有语言都有平等的权利,可以跨盒子边界无缝互动,而Motoko只是众多选择中的一种。这对于使互联网计算机平台成为一个开放的平台是很重要的。

到目前为止,WASM已被证明是实现这一目标的通用代码格式。我们尤其受益于它的简单性、模块性以及安全和确定性语义。但是,尽管有这些很好的属性,移植编译器和库,更不用说不同Wasm生态系统之间的应用程序了,并不像人们希望的那样简单,因为它涉及的不仅仅是裸代码。但Wasm还很年轻,预计会遇到某些障碍。

我们热切期待的最大的即将到来的Wasm特性是一流引用类型和函数引用的出现。这将产生一个更干净的系统API,Wasm模块(以及由此产生的Motoko程序)通过它与Internet计算机平台通信)。感兴趣的程序员可以在这里找到更多关于SDK的详细信息,并通过GitHub在这里为Motoko基础库做出贡献。

安德烈亚斯·罗斯伯格(Andreas Rossberg)是DFINITY的研究员和工程师,他领导开发Motoko,这是DFINITY互联网计算机的一种新的编程语言。在加入DFINITY之前,他是Google的工程师,致力于开发在Chrome上运行的V8 JavaScript引擎,还在马克斯·普朗克软件系统研究所(Max Planck Institute For Software Systems)担任编程语言理论、设计和实现方面的学术研究员。他是WebAssembly的联合设计者之一,并编写了其规范。下面,他讨论了DFINITY在互联网计算机上使用WebAssembly以及设计Motoko的经验。

标签:互联网计算机、Motoko、wasm、web组装