我们很高兴地宣布Ruby3的新类型签名语言RBS。Ruby3长期声明的目标之一是添加类型检查工具。在与Matz和Ruby提交者团队进行了大量讨论之后,我们决定逐步增加一种名为“RBS”的基础类型签名语言,该语言将随Ruby3一起发布,并附带stdlib的签名。RBS命令行工具也将随Ruby3一起提供,因此您可以为自己的Ruby代码生成签名。
类型化与非类型化对于编程语言来说是一个有30年历史的问题。类型化语言适用于较大的项目,但通常灵活性较差。非类型化语言允许快速开发,但使用它们扩展团队和代码库可能很困难。
编程语言设计人员意识到了这些权衡,并试图结合其他语言的功能来抵消这一点。C#有一个称为动态的功能,它可以将类型检查从编译时延迟到运行时。在编译时允许赋值和读取任何类型的值,但可能会引发运行时错误以确保安全性。这几乎等同于非类型化语言!反之亦然?我们看到非类型化语言类型检查选项(PHP、Python)。我们也有生产中使用的非类型化语言的类型化方言(打字稿)。
Matz早在四年前就宣布Ruby3将支持静态类型检查。在看到多个社区开发的类型检查器之后,Ruby提交者团队决定为社区构建类型检查器建立一个基础。Ruby3将提供为Ruby程序编写类型签名的能力,以及为Ruby标准库编写内置类型签名的能力。标准类型签名语言将使Ruby代码中的类型定义在类型检查器之间可移植,并鼓励社区为他们的gem和应用程序编写类型。
我们为Ruby3定义了一种名为RBS的新语言,用于类型签名。签名是在.rbs文件中编写的,这与Ruby代码不同。您可以认为.rbs文件类似于TypeScript中的.d.ts文件或C/C++/objc中的.h文件。拥有不同文件的好处是不需要更改Ruby代码就可以开始类型检查。您可以安全地选择加入类型检查,而无需更改工作流的任何部分。
#sig/merchant.rbsclass Merchant attr_read内标识:string attr_read名称:string attr_read Employees:array[Employee]def Initialize(Token:String,Name:String)->;void def each_Employee:(){(Employee)->;void}->;void|()->;枚举器[Employee,void]end。
Rbs文件定义了一个名为Merchant的类,它有助于读者理解该类的概述。
该类有三个属性Token、Name和Employees。令牌和名称的类型为字符串。RBS还支持像Array这样的泛型类,我们可以从Employees属性的type中看到这一点。它是一组员工。
RBS还描述了类中定义的方法及其类型。该类定义了Initialize和Each_Employee方法。Initialize方法需要将Token和Name作为关键字参数。Each_Employee方法接受一个块,或者返回一个枚举器实例。
RBS是一种描述Ruby程序结构的语言。它为开发人员概述了代码以及定义了哪些类和方法。最大的好处是可以根据实现及其执行来验证类型定义!
像Ruby这样的动态类型语言的类型系统开发不同于顺序静态类型语言。世界上已经有很多Ruby代码,Ruby的类型系统应该支持尽可能多的Ruby代码。
这迫使类型系统设计者在与现有代码兼容的复杂性和正确性上做出妥协。我们可能不得不引入类型检查器功能来支持现有Ruby代码中可能不正确的模式。但是,添加功能会使类型系统变得复杂且难于理解。因此,我们将重点放在最重要的代码模式上,以最大限度地降低类型系统的复杂性。
我们可以展示Ruby代码的两个重要特征,以及如何为它们指定类型。
鸭子类型是Rubysts中流行的编程风格,它假定对象将响应特定的方法集。鸭子类型的好处是灵活性。它不需要继承、混合或实现声明。如果一个对象有一个特定的方法,它就可以工作。问题是,这个假设隐藏在代码中,使得代码很难一目了然。
为了适应鸭子类型,我们引入了接口类型。接口类型表示一组独立于具体类和模块的方法。
如果我们想定义一个需要一组特定方法的方法,我们可以用接口类型编写它。
Interface_Appendable#需要`<;<;`运算符,该运算符接受`String`对象。Def<;<;:(String)->;voidend#传递`Array[string]`或`IO`起作用。#传递`TrueClass`或`Integer`不起作用。def append:(_Appendable)->;string。
这比传统的鸭子类型要好,因为它定义了类或模块期望实现的显式接口,并为文档和编辑器插件提供了提示,以便将以前隐式的接口公开为可靠的、可操作的文档。
非一致性是让表达式具有不同类型的值的另一种代码模式。它在Ruby中也很受欢迎,并介绍了:
CLASS COMMENT#注释可以由用户或Bot定义作者进行:()-&>;(User|Bot)#带/不带块的两个重载def Each_Reply:()->;枚举器[COMMENT,void]|{(COMMENT)->;void}->;void...end。
我们提供了一种编写类型的语言。那么,我们能对苏格兰皇家银行的文件做些什么呢?
以下是拥有类型的主要好处列表。我们可以在RBS文件中编写类型,这些工具将通过以下方式帮助您编写Ruby代码:
查找更多错误:我们可以检测到未定义的方法调用、未定义的常量引用,以及动态语言可能遗漏的更多内容。
Nil安全性:基于RBS的类型检查器具有可选类型的概念,即允许值为Nil的类型。类型检查器可以检查表达式是否为空,并为nil:NilClass`发现未定义的方法(save!)';。
更好的IDE集成:解析RBS文件可以让IDE更好地理解Ruby代码。方法名称完成运行得更快。动态错误报告检测到更多问题。重构可以更可靠!
引导式鸭子分类:接口类型可以用于鸭子分类。它帮助API用户更准确地了解他们可以做什么。这是一个更安全的鸭子类型版本。
当然,这些都不是免费的。我们如何为RBS构建工具,使开发人员可以更轻松地开始使用它?
我们在RBS之上开发了静态类型检查器。Stop是用Ruby实现的静态类型检查器,它基于RBS。Solbet是一个静态类型检查器,它有自己的类型定义语言RBI,但计划在未来支持RBS。
我们还在开发和开发其他工具,以扩展苏格兰皇家银行的工具链。RBS运行时类型检查器是Ruby Google Summer of Code项目之一,它使用RBS类型签名来实现运行时类型检查。类型分析器是一个探索性项目,它基于一种称为抽象解释的程序分析技术,从Ruby源代码生成RBS文件。还有一个支持Rails的项目。
这篇文章介绍了RBS,这是Ruby3中用于类型的一个新部分。我解释了可以使用RBS编写的内容、RBS设计的关键概念,以及RBS带来的好处和工具。您为Ruby代码编写类型定义,我们的工具将分析您的代码。我们知道不是所有的Ruby爱好者都会改用类型化的Ruby,但是我们相信这值得在您的代码中尝试!
在Square,我们正在测试基于RBS的类型检查解决方案,并继续对其进行迭代。我们正在为一些内部项目编写RBS文件,并使用START进行代码类型检查。我们正在从.proto文件构建RBS生成器。
我期待着在几个月内分享这些实验的结果。