For faster navigation, this Iframe is preloading the Wikiwand page for 类型系统.

类型系统

电脑科学中,类型系统(英语:type system)用于定义如何将编程语言中的数值和表达式归类为许多不同的类型,如何操作这些类型,这些类型如何互相作用。类型可以确认一个值或者一组值具有特定的意义和目的(虽然某些类型,如抽象类型和函数类型,在程序运行中,可能不表示为值)。类型系统在各种语言之间有非常大的不同,也许,最主要的差异存在于编译时期的语法,以及运行时期的操作实现方式。

编译器可能使用值的静态类型以优化所需的存储区,并选取对值运算时的较佳算法。例如,在许多C编译器中,“浮点数”资料类型是以32 位元表示,与IEEE 754规格一致的单精度浮点数。因此,在数值运算上,C应用了浮点数规范(浮点数加法、乘法等等)。

类型的约束程度以及评估方法,影响了语言的类型。更进一步,编程语言可能就类型多态性部分,对每一个类型都对应了一个极度个别的算法的运算。类型理论研究类型系统,尽管实际的编程语言类型系统,起源于电脑架构的实际问题、编译器实现,以及语言设计。

基础

[编辑]

定型(typing,又称类型指派)赋予一组位元某个意义。类型通常和存储器中的数值或物件(如变量)相联系。因为在电脑中,任何数值都是以一组位元简单组成的,硬件无法区分存储器地址脚本字符整数、以及浮点数。类型可以告知程序和程式设计者,应该怎么对待那些位元。

类型系统提供的主要功能有:

  • 安全性
使用类型可允许编译器侦测无意义的,或者是可能无效的代码。例如,可以识出一个无效的表达式"Hello, World" + 3,因为不能对(在平常的直觉中)逐字字符串加上一个整数。强类型提供更多的安全性,但它并不能保证绝对安全(详情请见类型安全)。
  • 优化
静态类型检查可提供有用的资讯给编译器。例如,如果一个类型指明某个值必须以4的倍数对齐,编译器就有可以使用更有效率的机器指令。
  • 可读性
在更具表现力的类型系统中,若其可以阐明程式设计者的意图的话,类型就可以充当为一种文件形式。例如,时间戳记可以是整数的子类型;但如果程式设计者宣告一个函数为返回一个时间戳记,而不只是一个整数,这个函数就能表现出一部分文件的阐释性。
  • 抽象化(或模块化
类型允许程式设计者对程序以较高层次的方式思考,而不是烦人的低层次实现。例如,程式设计者可以将字符串想成一个值,以此取代仅仅是字节的数组。或者类型允许程式设计者表达两个子系统之间的接口。将子系统间交互时的必要定义加以定位,防止子系统间的通讯发生冲突。

程序通常对每一个值关系一个特定的类型(尽管一个类型可以有一个以上的子类型)。其它的实体,如物件模块、通讯频道、依赖关系,或者纯粹的类型自己,可以和一个类型关系。例如:

一个数值的类型
一个物件的类型
  • 种类
一个类型的类型

在每一个编程语言中,都有一个特定的类型系统,保证程序的表现良好,并且排除违规的行为。作用系统对类型系统提供更多细微的控制。

类型

[编辑]

:断言变量e的类型是int。

类型检查

[编辑]

类型检查所进行的检验处理以及实行类型的约束,可发生在编译时期(静态检查)或运行时期(动态检查)。静态类型检查是在编译器所进行语义分析中进行的。如果一个语言强制实行类型规则(即通常只允许以不丢失资讯为前提的自动类型转换)就称此处理为强类型,反之称为弱类型

静态和动态检查

[编辑]

如果一个编程语言的类型检查,可在不测试运行时期表达式的等价性的情况下进行,该语言即为静态类型的。一个静态类型的编程语言,是在运行时期和编译时期之间的处理阶段下重视这些区别的。如果程序的独立模块,可进行各自的类型检查(独立编译),而无须所有会在运行时出现的模块的那些资讯,该语言即具有一个编译时期阶段。如果一个编程语言支持运行时期(动态)调度已标记的资料,该语言即为动态类型的。如果一个编程语言破坏了阶段的区别,因而类型检查需要测试运行时期的表达式的等价性,该语言即为依存类型的。[1]

在动态类型中,经常在运行时期进行类型标记的检查,因为变量所约束的值,可经由运行路径获得不同的标记。在静态类型编程语言中,类型标记使用识别联合类型表示。

动态类型经常出现于脚本语言RAD语言中。动态类型在解释型语言中极为普遍,编译语言则偏好无须运行时期标记的静态类型。对于类型和隐式类型语言较完整的列表参见类型和隐式类型语言。

术语推断类型鸭子类型,duck typing)指的是动态类型在语言中的应用方式,它会“推断”一个数值的类型。

看看类型标记检查是如何运作的,考虑下列假码示例:

var x; //(1)
x := 5; //(2)
x := "hi"; //(3)

在这个示例中,(1)宣告x;(2)将整数值5代给x;(3)将字符串值"hi"代给x。在主要的静态系统中,这个代码片断将会违反规则,因为(2)和(3)对 x所约束的类型相矛盾。

相较之下,一个纯粹的动态类型系统允许上述程序的运行,因为类型标记附到数值上(不是变量)。在处理错误语句或表达式的时候,以动态类型实现的语言会捕捉程序的错误,而不是误用错误类型的数值。换句话说,动态类型捕捉在程序运行时的错误。

典型的动态类型实现,会以类型标记维持程序所有数值的“标记”,并在运算任何数值之前检查标记。例如:

var x := 5; //(1)
var y := "hi"; //(2)
var z := x + y; //(3)

在这个程序片断中,(1)将数值5约束给x;(2)将数值"hi"约束给y;以及(3)尝试将x加到y。在动态类型语言中,约束给x的值会是一对整数, 5),且约束给y的值会是一对字符串, "hi")。当这个程序尝试运行第3行时,语言对类型标记整数字符串进行检查,如果这两个类型的+(加法)运算尚未定义,就会发出一个错误。

某些静态语言有一个“后门”,在这些编程语言中,能够编写一些不被静态类型所检查的代码。例如,Java和C-风格的语言有“转型”可用。在静态类型的编程语言中,不必然意味着缺乏动态类型机制。例如Java使用静态类型,但某些运算需要支持运行时期的类型测试,这就是动态类型的一种形式。更多静态和动态类型的讨论,请参阅编程语言

实践中的静态和动态类型检查

[编辑]

对静态类型和动态类型两者之间的权衡也是必要的。

静态类型在编译时期时,就能可靠地发现类型错误。因此通常能增进最终程序的可靠性。然而,有多少的类型错误发生,以及有多少比例的错误能被静态类型所捕捉,目前对此仍有争论。静态类型的拥护者认为,当程序通过类型检查时,它才有更高的可靠性。虽然动态类型的拥护者指出,实际流通的软件证明,两者在可靠性上并没有多大差别。可以认为静态类型的价值,在于增进类型系统的强化。强类型语言(如MLHaskell)的拥护者提出,几乎所有的bug都可以看作是类型错误,如果编写者以足够恰当的方式,或者由编译器推断来宣告一个类型。[2]

静态类型通常可以编译出速度较快的代码。当编译器清楚知道所要使用的资料类型,就可以产生优化过后的机器代码。更进一步,静态类型语言中的编译器,可以更轻易地发现较佳快捷方式。某些动态语言(如Common Lisp)允许任意类型的宣告,以便于优化。以上理由使静态类型更为普及。参阅优化

相较之下,动态类型允许编译器和解译器更快速的运作。因为原始码在动态类型语言中,变更为减少进行检查,并减少解析代码。这也可减少编辑-编译-测试-调试的周期。

静态类型语言缺少类型推断(如Java),而需要编写者宣告所要使用的方法或函数的类型。编译器将不允许编写者忽略,这可为程序起附加性帮助文档的作用。但静态类型语言也可以无须类型宣告,所以与其说是静态类型的代价,倒不如说是类型宣告的报酬。

静态类型允许构造函数库,它们的用户不太可能意外的误用。这可作为传达函数库开发者意图的额外机制。

动态类型允许建构一些静态类型系统所做不出来的东西。例如,eval函数,它使得运行任意资料作为代码成为可能(不过其代码的类型仍是静态的)。此外,动态类型容纳过渡代码和原型设计,如允许使用字符串代替数据结构。静态类型语言最近的增强(如Haskell 一般化代数资料类型)允许eval函数以类型安全的方式撰写。

动态类型使元编程更为强大,且更易于使用。例如C++模板的写法,比起等价的RubyPython写法要来的麻烦。更高度的运行时期构成物,如元类别(metaclass)和内观(Introspection),对静态类型语言而言通常更为困难。

强类型和弱类型

[编辑]

强类型的基本定义即为,禁止错误类型的参数继续运算。C语言的类型转换即为缺乏强类型的证例;如果编写者用C语言对一个值转换类型,不仅令编译器允许这个代码,而且在运行时期中也同样允许。这使得C代码可更为紧密和快速,不过也使调试变的更为困难。

部分学者使用术语存储器安全语言(或简称为安全语言)形容禁止未定义运算发生的语言。例如,某个存储器安全语言将会检查数组边界

弱类型意指一个语言可以隐式的转换类型(或直接转型)。看看先前的例子:

var x := 5;
var y := "37";
x + y;

在弱类型语言中编写上述代码,并不清楚将会得到哪一种结果。某些语言如Visual Basic,将会产生可以运作的代码,它将会给出的结果是42:系统将字符串"37"转换成数字37,以符合运算上的直觉;其它的语言,像JavaScript将会产生的结果是"537":系统将数字5转换成字符串"5"并把两者串接起来。在Visual Basic和JavaScript中,最终的类型是以那两个操作数为考量的规则所决定。在部分语言中,如AppleScript,某个值最终的类型,只以最左边的操作数的类型所决定。

设计精巧的语言也允许语言显现出弱类型(借由类型推断之类的技术)的特性以方便使用,并且保留了强类型语言所提供的类型检查和保护。例子包括VB.NetC#以及Java

运算符重载所带来的简化,像是不以算术运算中的加法来使用“+”,可以减少一些由动态类型所造成的混乱。例如,部分语言使用“.”或“&”来串连字符串。

类型系统的安全性

[编辑]

编程语言的类型系统的第三种分类方法,就是类型运算和转换的安全性。如果它不允许导致不正确的情况的运算或转换,电脑科学就认为该语言是“类型安全”的。

再次看看这个假码例子:

var x := 5;
var y := "37";
var z := x + y;

在一个如Visual Basic的语言中,例子中的变量z得到的值为42。不管编写者有没有这个意图,该语言定义了明确的结果,且程序不会就此崩溃,或将不明定义的值赋给z。就这方面而言,这样的语言就是类型安全的。

现在来看C的相同例子:

int x = 5;
char y[] = "37";
char* z = x + y;

在这个例子中,z将会指向一个超过y地址5个字节的存储器地址,相当于指向y字符串的指针之后的两个空字符之处。这个地址的内容尚未定义,且有可能超出存储器的寻址界线,而且就这么引用参考z会引起程序的终止。虽是一个良好类型,但却不是存储器安全的程序——如果以对类型安全语言而言不该发生为先决条件的话。

多态性和类型

[编辑]

术语“多态性”指的是:代码(尤其是函数和类别)对各种类型的值能够动作,或是相同数据结构的不同实体能够控制不同类型的元素。为了提升复用代码的潜在价值,类型系统逐渐允许多态性:在具有多态性的语言中,程式设计者只需要实现如列表或词典的数据结构一次,而不是对使用到它的元素的每一个类型都规划一次。基于这个原因,电脑学家也称使用了一定的多态性的方法为泛型程式设计。类型理论的多态性基础与抽象化模块化和(偶尔)子类型有相当密切的联系关系。

推断类型

[编辑]

推断类型(鸭子类型,Duck typing)最初是由Dave Thomas在Ruby社群中提出的,推断类型用了这个论证法“如果它像什么,而且其它地方也像什么,那么它就是什么。”

在某些程式设计环境中,两个物件可以有相同的类型,即使它们没有什么交集。一个例子是C++迭代器和指针所拥有的的双重性,两者皆以不甚相同的机制实现并提供一个* 运算。

这个技术之所以常被称作“鸭子类型”,是基于这句格言:“如果它摇摇摆摆的走法很像鸭子,而且它的嘎嘎叫声也像鸭子,那它就是一只鸭子!”

"If it waddles like a duck, and quacks like a duck, it's a duck!"

显示宣告和隐式暗示

[编辑]

许多静态类型系统,如C和Java,要求要宣告类型:编写者必须以指定类型明确地关系到每一个变量上。其它的,如Haskell,则进行类型推断:编译器根据编写者如何运用这些变量,以草拟出关于这个变量的类型的结论。例如,给定一个函数f(x,y),它将xy加起来,编译器可以推断出xy必须是数字——因为加法仅定义给数字。因此,任何在其它地方以非数值类型(如字符串或链表)作为参数来调用f的话,将会发出一个错误。

在代码中数值、字符串常量以及表达式,经常可以在详细的前后文中暗示类型。例如,一个表达式3.14可暗示浮点数类型;而[1, 2, 3]则可暗示一个整数的链表;通常是一个数组

类型的类型

[编辑]

类型的类型是一种种类。在类型程式设计中有明确的种类,如Haskell编程语言类型构造函数,在申请比较简单的类型之后,其返回一个简单的类型。例如,类型构造函数二选一有这些种类* -> * -> **代表种类),而且它的申请二选一字符串整数是一个简单的类型。然而,大多数编程语言的类型,是由编写者来暗示或硬编码,这就并未将种类的概念用作为首选层。

类型可分为几个大类:

这是最简单的类型种类,例如:整数浮点数
全部是数字的类型,例如:整数和自然数
以浮点数表示数字的类型
由基本类型组合成的类型,例如:数组记录单元抽象资料类型具有复合类型和界面两种属性,这取决于你提及哪一个。
例如:变量类型
例如:双参函数
如参数化类型、类型变量
模块
  • 精炼类型
识别其它类型的子集的类型
取决于运行时期的数值的类型
  • 所有权类型
描述或约束面向对象系统结构的类型

兼容性:等价性和子类型

[编辑]

对于静态类型语言的类型检查器,必须检验所有表达式的类型,是否与前后文所期望的类型一致。例如指派语句x := e,推断表达式e的类型,必定与宣告或推断的变量类型x一致。这个一致性的概念,就称为兼容性,是每一个编程语言所特有的。

很明显,如果ex的类型相同,就允许指派,然后这是一个有效的表达式。因此在最简单的类型系统中,问题从两个类型是否兼容,简化为两个类型是否相等(或等价)。然而不同的语言对于两个类型表达式是否理解为表示了相同类型,有着不同的标准。类型的相等理论的差异相当巨大,两个极端的例子是结构类型系统(Structural type system),任两个以相同结构所描述的值的类型都是等价的,且在标明类型系统(Nominative type system)上,没有两个独特的语法构成的类型表达式表示同一类型,(类型若要相等,就必须具有相同的“名字”)。

子类型的语言中,兼容关系更加复杂。特别是如果AB的子类型,那么类型A的值可用于类型B也属意料之中,但反过来就不是这样。如同等价性,对每一个编程语言而言,子类型的关系的定义是不同的,可能存在各种变化。在语言中出现的参数或者特定的多态性,也可能意味着具有对类型的兼容性。

争议

[编辑]

在强类型、静态类型语言的支持者,和动态类型、自由形式的支持者之间,经常发生争执。前者主张,在编译的时候就可以较早发现错误,而且还可增进运行时期的性能。后者主张,使用更加动态的类型系统,分析代码更为简单,减少出错机会,才能更加轻松快速的编写程序。[3]与此相关的是,考虑到在类型推断的编程语言中,通常不需要手动宣告类型,这部分的额外开销也就自动降低了。

参考文献

[编辑]
  1. ^ Harper, Robert & Benjamin C. Pierce (2005), "Design Considerations for ML-Style Module Systems", in Pierce, Benjamin C., Advanced Topics in Types and Programming Languages, Cambridge, MA: MIT Press, ISBN 0262162288页面存档备份,存于互联网档案馆
  2. ^ 存档副本. [2007-03-24]. (原始内容存档于2008-05-12). 
  3. ^ Meijer, Erik and Peter Drayton. Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages (PDF). Microsoft Corporation. (原始内容 (PDF)存档于2007-02-16). 

参阅

[编辑]
{{bottomLinkPreText}} {{bottomLinkText}}
类型系统
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.

X

Get ready for Wikiwand 2.0 🎉! the new version arrives on September 1st! Don't want to wait?