How Gradual Typing System Helps Us
作者 | Liyi
整理 | Hana
作者介绍:
Liyi, from Huawei, inc. 2012 Lab, OS Kernel Lab, working as
- verification engineer, and
- verification tool developer
Hiring @Beijing @Shanghai
写给不想看完全文的同仁们,以下是本文核心观点:
- 类型系统的本质是提供信息,这些信息不仅仅是提供给编译器看的,也是提供给人看的
- 站在工程视角,强大的类型系统并非银弹
- 强大的类型系统带来更高的开发门槛,更低的迭代速度和原型 demo 的能力(个人观点,可探讨)
- 过弱的/缺失类型系统支撑将极大降低开发质量和可维护性
- 没有完美的类型系统(和语言),只有最适合的
- 站在我们的场景下,Python 的渐进类型系统就是一个比较合适的类型系统
- 我们很喜欢 Trait (Rust/Haskell),所以我们把 Trait 移植到了 Python
视频回顾在此:
渐进式类型系统如何帮助我们,以及如何在工程中改造Python渐进的类型系统
以下正文。
对于一个普通的开发者,类型对我们意味着什么?
一句话归纳:我认为 类型 是附着于程序之上的额外信息。类型系统的表达能力高低,则意味着通过类型能够提供给我们的信息的多少。高级的类型系统如精化类型系统,依赖类型系统等能够提供很多重要信息。但是这也会付出代价:类型检查变得更为困难。
今天想和大家一起聊一下,渐进式类型系统在我们的工程开发中是怎么样对我们产生帮助的,以及我们在工程实践中是怎么对 Python 的渐进式类型系统进行改造的。
我们先从一个小例子开始。在操作系统中,会有很多数据结构,这些数据结构是与 CPU 的核相关的,比如每个核的状态,每个核上在跑什么样的任务等等。这个时候每一个核有一个序号,我们称它是这个核的 id。id 的值的合法范围取决于硬件体系结构。假设我们有一个 8 核的 CPU,那这个核的号数应该在 0~7 内,即从 0 号核到 7 号核。当然,这个数字还得是一个整数。那么,当我们想要定义一个数据结构来描述这样一个 id 时,我们就需要有一个类型。
以 C 语言为例,最简单粗暴的定义方式,CoreID
实际上就是一个普通的整数。
// 1st definition
// It is a number ...
typedef int CoreID;
但是,CoreID
其实仅需要满足 0~7 这个范围,远小于 int
能够表示的内容,这时我们会认为这样定义的类型有点浪费。进一步地,我们可以把 CoreID
定义成一个 unsigned char
,即一个无符号的 8 位整数。这样,CoreID
的范围就可以很小,同时其保存的整数是非负的(可见在这个过程中,我们增加了这个类型所抱有的信息量)。
// 2nd definition
// Small, and not negative
typedef unsigned char CoreID;
但是,我觉得这个定义还不够精确,我想要进一步细化一下 CoreID
,这时我们就可以引入 Refinement Type。如有兴趣可以阅读这篇 精化类型简介。
R e f i n e m e n t T y p e s = T y p e s + P r e d i c a t e s Refinement Types = Types + Predicates RefinementTypes=Types+Predicates
下面这段代码就是 LiquidHaskell 1 (a Refinement Type Checker for Haskell) 里 Refinement Type 定义方式。我们将 CoreID
定义为一个集合,集合的基本类型是一个整数(Int
),集合里的每个元素都要在 0~7 范围内。
-- 3rd definition
-- and, satisfies a certain constraint