浅谈编程语言的类型系统
类型系统(type system)是一门编程语言最核心也是最基础的部分。无论该语言基于何种编程范式,都必须在开天辟地之初首先对类型系统作出明确的定义。这是因为,编程语言虽然五花八门,千奇百怪,但是归根结底,编程语言最终的目标,本质上无非是回答两个问题:
- 如何表示信息;
- 如何处理信息。
无论是面向过程的编程语言、面向对象的编程语言、函数式编程语言、并行编程语言或者其他任何千奇百怪的编程语言,其根本性的终极目标,就是回答以上两个问题。各种编程语言之所以差异颇大,其实就是对这两个问题给出的答案不同导致的。
在如何表示信息这一问题上,编程语言通常需要定义一些“基本存储单元”,作为整个语言世界的基本构成要素。这种思想很类似于我们对物理世界的认识——宇宙虽然鬼斧神工,丰富多彩,但是在微观上,整个世界仅仅是由少数寥寥几种基本粒子构成的(物理细节不必深究,这里只是打个比方)。但是奇怪的是,基本粒子就只有几种,为何却能构成地球、水、人、树、风这些看似截然不同的东西呢?答案在于,基本粒子虽然不多,但是自然界确立了一套简单而精妙的组合规则,使得基本粒子能够以许多种不同的方式组合在一起,由于组合方式的不同(结构差异),组合规模的不同(数量差异),导致了最终宏观表现的不同。
与现实物理世界类似,一门编程语言就确立了一个独特的“世界”,这个世界可能丰富多彩,千奇百怪。但是就如我们现实世界一样,繁杂的外表之下,骨子里都是由一些“基本粒子”,按照一定的组合方式构成的。那么究竟有哪些基本粒子,又允许进行何种组合,对编程语言所确立的世界最终的宏观结果影响非常巨大。甚至可以说是根本性的。
有一定编程经验的程序员,往往对类型系统不太关心。他们更感兴趣的是语言的其他特性,例如并行计算能力,编程风格,类库等等。这些特性当然非常重要,就生产环境的应用来说,语言特性甚至是处于次要地位的。类库被许多程序员认为比语言本身更重要。然而,坚实的应用是以对语言深刻的理解为基础的,花费一些时间对语言的本质进行研究,会对深入理解语言背后的设计考虑有很大的帮助。也能让我们避免陷入语言的陷阱,或者陷入与别人的口水战之中。
回到对类型系统的考虑,那么究竟什么是类型系统呢?
一门语言定义了一套基本类型的“集合”,这个集合就作为一个整体被称为类型系统。这一称谓中,涉及到两个关键词——“类型”和“系统”。
什么是“类型”?
计算机存储是以二进制方式进行的,并以连续的八个二进制位为一个基本单元——“字节”。从这一点来看,计算机存储是通用的,存储人类的文字或者存储图像、声音或其他别的媒介都没有内在的本质差异。但是奇怪的是,在编程语言概念上,却总是会引入一系列的大相径庭的“基本类型”。例如,int和double,一个存储整数,一个存储浮点数。如果我们考虑一下,就会发现其实int和double本身并没有什么差别,都只不过是若干个字节构成的存储单元而已。那么对他们进行区分意义何在?其实就“存储”概念而言,我们用二进制方式,以字节为单位来实现信息的存储,已经给出了信息表示的“终极答案”了。但是完成存储,只是整个计算机系统功能的一部分。如果仅仅是把信息存储,而不进行计算,那么计算机就不叫计算机,改叫存储机了。可见,问题的实质在于,我们要存储,更要计算。
int和double都是几个字节构成的,但是其运算规则截然不同,差异巨大。同样的,string和int或double也不相同。我们在string上进行拼接、删减、搜索、替换。但是在int上进行加减乘除。这些计算需求和内部实现上的差异,迫使我们的语言层次上进行明显的区分。
上面举的类型,都是来自C家族语言中的概念。但是我们也知道,在一些语言中,不需要我们对类型进行显式的说明。但是不说明不代表不存在。只不过一个是程序员显式声明,一个是编译器(或解释器)自主推导;一个是把责任推给程序员,一个是把责任推给编译器作者。类型系统总是内在的存在的。永远没有被去除。
什么是“系统”?
坦白讲,这是一个非常模糊的概念。我们会说操作系统、消化系统、生态系统……各种各样的系统,然而对于系统本身是什么,在不同的科学领域有截然不同的定义。通常我们所说的系统中,存在一些基本要素(软件模块、细胞、物种等等),然后存在一定的相互作用关系(函数调用、细胞连接、捕食与被捕食等等),在此基础上实现一定的功能(完成金融计算、排解人体毒素、完成有机物的自然循环等等)。那么我们就把这些基本元素,以及其构成方式,统称为一个系统。
之所以说“系统”是个模糊的概念,原因在于,这一概念本身并非原子概念,一个系统,也可能再分解为一系列的子系统,例如操作系统就可以分为输入输出子系统,绘图子系统等等,人体内的消化系统也能够分解为一系列的子系统。而子系统又可在更小的级别进行分解。系统的划分是相对的,系统的构成也是相对的,因此其本身常常是模糊的。
这么说来,如果要追究系统一词的内涵,会很困难。但是在我们讨论的编程语言这一领域内,当提到“类型系统”时,系统其实就是指:
- 一组基本类型构成的“基本类型集合”;
- “基本类型集合”上定义的一系列组合、运算、转换方法。
这两点合起来,就成为了我们的“类型系统”。只要做到这两点,就已经非常强大了。这其中,“基本类型集合”是一个非常小的有限集合,也就寥寥几个元素,而“组合、运算、转换”等规则,也是一个较小的有限集合。但是通过选择不同的元素进行组合,这两个有限的集合之上,却衍生出了一个无限集合——“类型空间”。
理解这一点非常关键。因为这恰好符合了我们对自然界构成的认识——有限的若干种基本粒子,有限的若干种基本规则,结果却是无限可能性的巨大世界。
这一简单优雅而惊人的世界构成观,贯穿了人类现实世界和计算机编程语言所定义的虚拟世界。或许语言的设计者也没有料想到,但是最终的结果确实是有限的设计导出了无限的可能性。
所以,当认识到这一点之后。就再也不会轻视类型系统,再也不会把类型系统看得简单,自以为十分了解了。而类型系统设计上的细微差异,最终也会导致截然不同的类型空间,导致对信息表达方式的巨大差异。那么还有什么理由不认真的研究这一基本要素的呢?还有什么理由让我们逃开这一许多程序员认为“简单”的主题呢?