Motoko,一种专为互联网计算机设计的编程语言,现已开源
DFINITY 基金会的 Motoko 团队很高兴地宣布Motoko 编译器、解释器、测试套件和文档的开源。自公开发布以来经过三年半的开发,Motoko 的完整源代码现在可以在 Apache 2.0 许可下使用(连同其先前的开源基础库)。
Motoko 是一种编程语言,旨在无缝支持互联网计算机的编程模型,从而可以更轻松地高效构建应用程序并利用该平台的一些更独特的功能。Motoko 是强类型的、基于参与者的,并且具有对正交持久性和异步消息传递的内置支持。
生产力和安全特性包括自动内存管理、泛型、类型推断、模式匹配以及任意精度和固定精度算术。消息传递透明地采用互联网计算机的 Candid 接口定义语言和有线格式,以实现类型化、高级和跨语言的互操作性。
我们希望此代码版本能够促进与更广泛社区的合作和贡献,无论是改进文档、完善错误消息,还是开发全新的工具,例如额外的 IDE 集成、调试器支持和代码格式化工具。
我们的目的是为内部和外部贡献者提供相同的开发体验。目前,我们的测试基础设施仍然部分依赖于内部服务,但我们正在努力将它们替换为可公开访问的服务。
外部和内部开发人员将处于平等地位,毫无疑问,当我们进入开放空间时,我们会遇到一些成长的烦恼,请耐心等待。
对于那些还不熟悉 Motoko 的人,这里简要介绍一下它是什么、我们为什么开发它以及它是如何工作的……
WebAssembly
要理解 Motoko,我们首先必须简要介绍一下 WebAssembly - 又名 Wasm(是的,正确拼写,没有全部大写)。您可能知道,Wasm 是一种新的低级代码格式,旨在实现可移植性、安全性和高效性。
它最初的用例是网络,但这个名字实际上是用词不当:当我们在 W3C 工作组设计 Wasm 时,我们小心翼翼地将它作为一个开放标准和通用平台。也就是说,它不针对任何特定的编程语言、范式、计算环境或平台,并且我们确保它完全不与网络绑定。
因此,Wasm 在许多其他环境中得到采用绝对不是偶然的,例如云计算、边缘计算、移动、嵌入式系统、物联网和区块链。
Wasm 有很多设计考虑因素,有些是显而易见的,有些则相当微妙。太多了,无法进入这里。
关于 Wasm 的技术目标、设计选择、形式语义和实现技术的相当全面的讨论可以在我们发表在 ACM 通信中的一篇科学文章中找到(这篇文章的旧版和技术性更强的版本可免费访问)。
Wasm 与其他虚拟机的主要区别在于,它没有针对任何特定的编程语言进行优化,而只是抽象了底层硬件,字节码直接对应于现代 CPU 的指令和内存模型。
最重要的是,Wasm 通过强大的模块化和严格的数学规范支持沙盒,确保执行是安全的,没有未定义的行为,并且(几乎)完全确定。而且,这些属性实际上有经过机器验证的数学证明!
总之,这些特性旨在使 Wasm 成为对便携性、安全性、通用性和性能有很高期望的各种环境和用例的有吸引力的选择 - 例如互联网计算机。
Wasm 的特性使其成为表示在互联网计算机上运行的程序的明显选择。但在实践中,将现有的编程语言移植到 Wasm 并非易事。显然,它需要实现一个新的编译器后端。
这很有趣,但努力并不止于此:它还需要移植语言的运行时系统和库原语。还有一些特性,尤其是与更高级语言相关的特性,目前无法在 Wasm 中轻松实现——例如:线程、协程、异常和尾调用。
虽然各种用各自的功能丰富 Wasm 的提案即将出现,但它们尚未最终确定标准化。
尽管已经有许多针对 Wasm 的实验性语言实现,但大多数还没有准备好迎接黄金时段。主要包括低级系统语言,如 C/C++ 和 Rust。
这些对于它们的用例来说当然是很好的,但它们不是为互联网计算机开发高级应用程序的理想工具,在互联网计算机中,可访问性、生产力和高保证往往比手动干预内存管理更可取。
同时,互联网计算机的语言需要提供对平台主要概念的访问:具有异步消息传递的分布式编程模型、cycles(又名gas)等资源概念以及其他一些特性。
当然,它们都可以作为库提供,但是一种本身包含适当结构的语言可以提供更加无缝的编程体验。
因此,如果我们无论如何都必须开展工作才能开始工作,为什么不将其应用于创建可以提供最佳用户体验并传达我们对互联网计算机编程的愿景的东西呢?
Motoko
这就是为什么 - 尽管有创建另一种语言的所有风险 - 我们决定创建Motoko。我们想要一种安全、易于使用、无缝公开平台概念的语言,以及一种对大多数程序员来说看起来足够友好且易于访问的语言。
目前,后一个目标使其几乎不可避免地处于分号和大括号语言阵营中,并且这个营地中不存在合适的语言。
但是 Motoko 相当传统的皮肤只是表面的:它的内部是一种现代语言。例如,每个构造都是一个表达式,它有闭包,它有变体类型和静态检查模式匹配,它有垃圾收集。
当然它有一个实际上健全的灵活的类型系统,即它确实保证不存在某些错误,例如崩溃、未定义的行为、曲解数据或只是在 switch 中遗漏了一个 case。没有漏洞!
同时,我们有意尝试不花哨或重新发明轮子,而是建立在丰富的实践和理论历史的基础上,并承认在该领域几十年来吸取的经验教训。
除了将易于理解的功能组合在一起之外,Motoko 的设计还包含许多小决策以最大限度地减少脚步和安全方面的错误,例如,默认情况下数字不能溢出,默认情况下局部变量是不可变的,默认情况下并发执行是原子的,默认情况下不能出现 null,默认情况下字段是私有的,等等。哦,没有继承,只有子类型。
实现 Motoko 的这些部分并将它们编译为 Wasm 是传统的编译器工艺。用 OCaml 编写的 Motoko 编译器使用类型化的中间表示,经过几次转换,并输出 Wasm 字节码。
生成的 Wasm 模块包含一个用 C 和 Rust 编写的小型运行时系统,主要实现一个简单的垃圾收集器,使用 Wasm 内存作为其堆。这并不难,但这里肯定有很大的改进潜力,我们正在努力。
Actors
然而,Motoko 的核心特征是它在语法和类型系统中对 actor 的直接支持。Actor 模型是一个众所周知的概念,已有 40 多年的历史,但遗憾的是,它几乎没有进入主流语言。
Actor 就像一个对象(在 Motoko 中,甚至看起来像一个对象),因为它封装了私有状态以及一组方法来处理可以发送给它的消息。但所有消息发送都是异步的。因此,与面向对象中的传统方法不同,actor 方法没有结果。
此外,所有消息都由一个参与者顺序接收 - 也就是说,它有一个隐式消息队列和方法原子地执行,即使消息是并发发送的。
Actors 是并发编程的一个很好的模型,因为它们会自动防止竞争条件(由于原子性和封装状态)和死锁(因为执行永远不会阻塞),因此排除了许多并发错误。所有这一切都不需要程序员定义锁。
Actors 也是分布式编程的一个很好的模型,因为异步自然会处理将消息发送到潜在的远程接收器所涉及的延迟。最后,actor 非常适合 Dfinity 的互联网计算机,其中应用程序以所谓的容器的形式部署 — 本质上,参与者表示为可以跨子网通信的 Wasm 模块。
因此,Motoko actor 编译为 Wasm 模块,其中方法成为具有平台定义的特殊约定的导出 Wasm 函数。
简而言之,Motoko 中的应用程序是一个(或多个)actor,它又是一个编译成 Wasm 模块的大型异步对象。使用 Wasm 的内存概念,这样的参与者可以立即管理多达 4 GiB 的内部状态,尽管可以通过链接多个 Wasm 模块来进一步扩大,每个模块都有自己的内存。
Futures
为了使异步编程更方便并允许以顺序“直接风格”表达它,Motoko 采用了另一个来自编程语言研究年鉴的 40 多年的想法,尽管幸运的是最近变得有点流行:Future(也称为某些社区的承诺)。
在 Motoko 中,它们以“异步值”的形式具体化 async<T>,即由带有 async 关键字前缀的表达式生成的类型值。特别是,函数体可以是一个异步表达式,从而自然地替换和概括存在于其他一些语言中的“异步函数”的更单一的概念。
有了这个,actor 方法毕竟是允许有结果的 - 只要这些是 Future。可以等待 Future 来获取它们的值,但只能在另一个异步表达式中,类似于其他现代语言中已知的 async/await monad。
Motoko 编译器通过传统的 CPS(延续传递风格)转换来实现这一点,将每个等待点转换为一个单独的 Wasm 函数(加上一些闭包环境),代表等待的延续。事实上,它是双管 CPS,因为每条消息也可以有一个带有各自失败延续的失败回复。
按照惯例,具有异步结果的方法是发送带有结果值作为参数的回复消息的方法。这个消息被创建的延续函数接收,然后它可以恢复它捕获的执行。等待回复不会阻止参与者 - 它可以在此期间自由接收其他消息。
Persistence
Motoko 的另一个重要考虑因素是允许开发人员利用区块链技术,而无需学习全新的计算类型。因此,我们删除了您可能需要的有关当前区块链编程语言种类的大部分特殊知识。
例如,没有可观察到的区块或区块高度的概念,没有用于更新区块链状态的显式构造,也没有用于将数据写入持久存储(如文件或数据库)的其他 API(尽管可以将其模拟为库)。
相反,互联网计算机实现了正交持久性 - 另一个旧想法,其中程序具有“永远”运行的错觉,并且它的内存只是保持活动状态(至少在它被明确删除之前)。
在 Motoko 中,这意味着开发人员不必担心在消息之间明确保存他们的数据,也不必担心文件或外部数据库:存储在程序变量中的任何值或数据结构在下一条消息到达时仍然存在,即使那是几个月后。
该平台负责在方法调用之间透明地保存和恢复容器的私有状态。这相对容易改装到 Wasm 引擎上,因为 Wasm 模块的状态在模块的内存、全局变量和表中显然是隔离的。
在大多数情况下,使用操作系统公开的虚拟内存技术观看 Wasm 内存就足够了。通过这种方式,平台知道此类内存中的页面何时被修改,并且可以采取任何必要的措施来持久化脏页面,以及为分布式共识协议散列它们。
Motoko 之外:Candid 的接口定义
因为互联网计算机运行 Wasm,所以 Motoko 只是创建应用程序的一种选择 - 而且是有意如此。我们还提供 Rust,我们期待提供其他语言选择。
即便如此,因为每种语言都会统一编译为以 Wasm 表示的容器,所以这些容器可以通过消息发送自由地相互通信,而不管它们的源语言如何。
为了明确定义这种互操作性,我们引入了一种名为 Candid 的通用接口定义语言(IDL)。它是互联网计算机上通信的通用语言,完全独立于 Motoko。它描述了容器理解的一组消息以及随同发送的数据类型。
在 Candid 中,数据通过独立于 Motoko 类型系统或任何其他编程语言的规范数据类型(数字、文本、数组、记录、变体、函数、对其他容器的引用)的组合进行描述。
呼,又是一个类型系统?好吧,程序员可能会很高兴 Motoko 编译器可以自动使用和生成这样的接口描述,用于 actor 导出和导入,并将它们映射到相应的 Motoko 类型。
它还自动生成正确的 Wasm 代码来序列化和反序列化每条消息的参数数据,透明地将 Motoko 的内部表示与 Candid 指定的二进制格式相互转换。
通过这种方式,Motoko 程序可以以有类型的方式与外部容器进行通信,并将远程调用表达为程序中的本地对象。
这与远程容器是用 Motoko 还是 Rust 编写的无关,容器的接口描述足以作为类型信息。除了单纯的方便之外,接口还提供了一种强大的模块化形式,其中程序可以针对其他参与者/容器进行类型检查,而无需访问它们的实现。
结论
我们的目标是让所有语言在互联网计算机上拥有平等的权利,所有语言都在 Wasm 中编译为容器,并且所有语言都通过 Candid 进行无缝通信。
这对于打开互联网计算机很重要,Motoko 只是众多选择中的一种,但它旨在成为在互联网计算机上开发的各种应用程序的特别好选择。
开始在 sdk.dfinity.org 上构建并加入我们的开发者社区 forum.dfinity.org。
来源:DFINITY
翻译:Catherine
长按关注 DFINITY 微信公众号
随时答疑解惑
*添加小助手微信 comiocn 进交流社群