语言:选择合适的类型系统

2,609 阅读7分钟

1 选择适合的类型系统

人们谈论类型系统时,他们通常谈论的是静态类型。对于关心类型系统的人来说,动态类型并不是很有趣,因为它几乎什么都不保证。例如,变量 x 可以保存什么样的值.

1.1 错误检测

静态类型检查最明显的好处是它可以及早发现一些编程错误。可以立即修复早期检测到的错误,而不是潜伏在稍后才被发现的代码中,当程序员在做别的事情时——甚至在程序已部署。

此外,这通常可以帮助更多地查明错误在类型检查期间比在运行时准确,当它们的效果可能不直到事情开始出错后的一段时间才变得可见。

在实践中,静态类型检查会暴露出惊人的广泛范围的错误。使用富类型语言工作的程序员经常说他们的一旦通过类型检查程序,程序往往会“正常工作”,往往比他们自认为他们有更多期待。

一种可能的解释这不仅是琐碎的心理失误(例如,忘记将字符串转换为取平方根之前的数字),但也有更深层次的概念错误 (例如,在复杂案例分析中忽略边界条件,或在科学计算中混淆单位),通常会表现为不一致类型的级别。

这种效果的强度取决于表现力类型系统和所讨论的编程任务:程序操纵各种数据结构(例如,符号处理应用程序例如编译器)为类型检查器提供比程序更多的购买只涉及一些简单的类型,例如科学中的数值计算应用程序(尽管,即使在这里,支持维度的精炼类型系统分析 [Kennedy, 1994] 可能非常有用)。

从类型系统中获得最大收益通常涉及一些程序员的注意力,以及愿意做为优秀语言所提供的工具;例如,一个复杂的将所有数据结构编码为列表的程序不会得到那么多来自编译器的帮助,以定义不同的数据类型或抽象每个键入。

表达型系统提供了许多编码“技巧”关于类型的结构信息。对于某些类型的程序,类型检查器也可以是非常宝贵的维护工具。例如,需要更改定义的一个复杂的数据结构,程序员不需要手动搜索来找到一个需要修复涉及此结构的代码的大型程序。

一旦数据类型的声明已更改,所有这些站点都变得类型不一致,只需运行编译器即可枚举它们并检查类型检查失败的点。

1.2 规范编程

类型系统支持编程过程的另一种重要方式是强制规范编程。

特别是,在上下文中在大规模软件组合中,类型系统构成了用于将大型组件打包和捆绑在一起的模块语言系统。

类型出现在模块(和相关结构)的接口中例如课程);事实上,一个接口本身可以被看作是“一个模块”,提供模块提供的设施的摘要——a实现者和用户之间的一种部分契约。

根据具有清晰接口的模块构建大型系统会导致一种更抽象的设计风格,设计和讨论界面独立于它们的最终实现。更抽象的思维关于接口通常会导致更好的设计。

1.3 文档

类型在阅读程序时也很有用。过程头和模块接口中的类型声明构成了一种文档形式,提供有关行为的有用提示。

此外,与嵌入的描述不同在评论中,这种形式的文档不会过时,因为它在每次编译器运行期间检查。类型的这种作用特别在模块签名中很重要。

不幸的是,术语“安全语言”是比“类型”更具争议性的系统。”尽管人们通常会在看到它时觉得自己认识,但他们对究竟什么是语言安全的概念受到强烈自我怀疑。

这种怀疑遍布他们所属的语言社区。不过,非正式地,安全的语言可以被定义为那些让自己无法编程时在自己脚下失火开枪的语言。

稍微提炼一下这种直觉,我们可以说一种安全的语言是保护自己的抽象的。每种高级语言都提供抽象的机器服务。安全性是指语言能够保证这些抽象和更高级别抽象的完整性 程序员使用语言的定义工具。

例如,一种语言可以提供具有访问和更新操作的数组,作为底层内存的抽象。

使用这种语言的程序员然后期望一个数组只能通过使用更新操作来改变 明确地写在上面——而不是,例如,写在其他的结尾数据结构。

类似地,人们期望词法范围的变量可以是只能从它们的范围内访问,调用堆栈的行为确实像堆栈等。

在安全的语言中,可以抽象地使用这种抽象;在一种不安全的语言,他们不能:为了完全理解一个程序可能(错误)行为,有必要记住各种低级细节,例如内存中数据结构的布局和顺序它们将由编译器分配。

在极限情况下,不安全的程序语言不仅会破坏它们自己的数据结构,甚至会破坏它们的数据结构。运行时系统;这种情况下的结果可能是完全任意的。语言安全与静态类型安全不同。

语言安全可以通过静态检查来实现,也可以通过运行时检查来实现无意义的操作,就在它们被尝试和停止的那一刻程序或引发异常。例如,Scheme 是一种安全语言,即使它没有静态类型系统。

相反,不安全的语言通常会提供“尽力而为”的静态类型检查器,帮助程序员至少消除最明显的错误类型,但是根据我们的定义,这样的语言也不符合类型安全的条件。

nition,因为他们通常无法提供任何形式的保证类型良好的程序表现良好——这些语言的类型检查器可以提示存在运行时类型错误(这当然总比没有更好)但不能证明他们的缺席。

	               Statically checked 	               Dynamically checked
	Safe           ML, Haskell, Java, go, etc. 			Lisp, Scheme, Perl, Postscript, python, etc.
	Unsafe 		C, C++, etc.

上表右下条目的空性说明事实上,一旦设施到位,可以确保大多数人的安全在运行时进行操作,检查所有操作几乎没有额外的成本。

(实际上,有一些动态检查的语言,例如,一些Basic 方言适用于具有最小操作系统的微型计算机,提供用于读取和写入任意内存位置的低级原语,这可能会被滥用来破坏运行时系统的完整性。) 运行时安全通常不能仅通过静态类型来实现。

例如,上表中列出的所有安全语言实际上都会动态执行数组边界检查。

同样,静态检查的语言有时会选择提供操作(例如,在 Java 中的向下转换运算符),其类型检查规则实际上是不健全的——语言通过动态检查这种构造的每次使用来获得安全性。

2 小结

某些语言,您不需要运行时信息,因为您的语言不执行运行时调度(或者您通过 vtables 或其他机制执行单个调度,因此不需要类型信息)。

对于某些语言,只需使用符号占位符就足够了,因为您只关心类型相等性,而不关心其名称或继承。

人们可能希望在您的类型系统中采用更多的形式主义。他们想知道你可以用它证明什么,而不是程序员可以用它做什么。

证明一些事情,这在学术界很常见。学者之所以做这样的事情,是因为类型系统中很容易出现缺陷,从而让事情偏离正确性。他们使用这些工具有可能发现其中一些错误。