类型系统

title: 基础.
content: 定型("typing",又称型别指派)赋予一组位元某个意义。型别通常和内存中的数值或物件(如变量)相联系。因为在电脑中,任何数值都是以一组位元简单组成的,硬件无法区分内存位址、指令码、字符、整数、以及浮点数。型别可以告知程序和程序设计者,应该怎么对待那些位元。  型别系统提供的主要功能有:  程序通常对每一个值关联一个特定的型别(尽管一个型别可以有一个以上的子型别)。其它的实体,如物件、模组、通讯频道、依赖关系,或者纯粹的型别自己,可以和一个型别关联。例如:  在每一个程序语言中,都有一个特定的型别系统,保证程序的表现良好,并且排除违规的行为。作用系统对型别系统提供更多细微的控制。
title: 类型.
content: formula_1:断言变量e的类型是int。
title: 型别检查.
content: 型别检查所进行的检验处理以及实行型别的约束,可发生在编译时期(静态检查)或执行时期(动态检查)。静态型别检查是在编译器所进行语义分析中进行的。如果一个语言强制实行型别规则(即通常只允许以不遗失资讯为前提的自动型别转换)就称此处理为强型别,反之称为弱型别。
title: 静态和动态检查.
content: 如果一个程序语言的型别检查,可在不测试执行时期运算式的等价性的情况下进行,该语言即为静态型别的。一个静态型别的程序语言,是在执行时期和编译时期之间的处理阶段下重视这些区别的。如果程序的独立模组,可进行各自的型别检查(独立编译),而无须所有会在执行时出现的模组的那些资讯,该语言即具有一个编译时期阶段。如果一个程序语言支援执行时期(动态)调度已标记的资料,该语言即为动态型别的。如果一个程序语言破坏了阶段的区别,因而型别检查需要测试执行时期的运算式的等价性,该语言即为依存型别的。  在动态型别中,经常在执行时期进行型别标记的检查,因为变量所约束的值,可经由执行路径获得不同的标记。在静态型别程序语言中,型别标记使用辨识联合型别表示。  动态型别经常出现于脚本语言和RAD语言中。动态型别在解释型语言中极为普遍,编译语言则偏好无须执行时期标记的静态型别。对于型别和隐式型别语言较完整的列表参见型别和隐式型别语言。  术语推断型别(鸭子类型,duck typing)指的是动态型别在语言中的应用方式,它会「推断」一个数值的型别。  看看型别标记检查是如何运作的,考虑下列假码范例:  在这个范例中,(1)宣告x;(2)将整数值5代给x;(3)将字串值"hi"代给x。在主要的静态系统中,这个代码片断将会违反规则,因为(2)和(3)对 x所约束的型别相矛盾。  相较之下,一个纯粹的动态型别系统允许上述程序的执行,因为型别标记附到"数值"上(不是"变量")。在处理错误语句或运算式的时候,以动态型别实作的语言会捕捉程序的错误,而不是误用错误型别的数值。换句话说,动态型别捕捉在程序执行时的错误。  典型的动态型别实作,会以型别标记维持程序所有数值的「标记」,并在运算任何数值之前检查标记。例如:  在这个程序片断中,(1)将数值5约束给x;(2)将数值"hi"约束给y;以及(3)尝试将x加到y。在动态型别语言中,约束给x的值会是一对(整数, 5),且约束给y的值会是一对(字串, "hi")。当这个程序尝试执行第3行时,语言对型别标记整数和字串进行检查,如果这两个型别的+(加法)运算尚未定义,就会发出一个错误。  某些静态语言有一个「后门」,在这些程序语言中,能够编写一些不被静态型别所检查的代码。例如,Java和C-风格的语言有「转型」可用。在静态型别的程序语言中,不必然意味着缺乏动态型别机制。例如Java使用静态型别,但某些运算需要支援执行时期的型别测试,这就是动态型别的一种形式。更多静态和动态型别的讨论,请参阅程序语言。
title: 实践中的静态和动态型别检查.
content: 对静态型别和动态型别两者之间的权衡也是必要的。  静态型别在编译时期时,就能可靠地发现型别错误。因此通常能增进最终程序的可靠性。然而,有多少的型别错误发生,以及有多少比例的错误能被静态型别所捕捉,目前对此仍有争论。静态型别的拥护者认为,当程序通过型别检查时,它才有更高的可靠性。虽然动态型别的拥护者指出,实际流通的软件证明,两者在可靠性上并没有多大差别。可以认为静态型别的价值,在于增进型别系统的强化。强型别语言(如ML和Haskell)的拥护者提出,几乎所有的bug都可以看作是型别错误,如果编写者以足够恰当的方式,或者由编译器推断来宣告一个型别。  静态型别通常可以编译出速度较快的代码。当编译器清楚知道所要使用的资料型别,就可以产生最佳化过后的机器码。更进一步,静态型别语言中的编译器,可以更轻易地发现较佳捷径。某些动态语言(如Common Lisp)允许任意型别的宣告,以便于最佳化。以上理由使静态型别更为普及。参阅最佳化。  相较之下,动态型别允许编译器和解译器更快速的运作。因为源代码在动态型别语言中,变更为减少进行检查,并减少解析代码。这也可减少编辑-编译-测试-除错的周期。  静态型别语言缺少型别推断(如Java),而需要编写者宣告所要使用的方法或函式的型别。编译器将不允许编写者忽略,这可为程序起附加性说明文件的作用。但静态型别语言也可以无须型别宣告,所以与其说是静态型别的代价,倒不如说是型别宣告的报酬。  静态型别允许建构函式库,它们的使用者不太可能意外的误用。这可作为传达函式库开发者意图的额外机制。  动态型别允许建构一些静态型别系统所做不出来的东西。例如,"eval"函式,它使得执行任意资料作为代码成为可能(不过其代码的型别仍是静态的)。此外,动态型别容纳过渡代码和原型设计,如允许使用字串代替资料结构。静态型别语言最近的增强(如Haskell 一般化代数资料型别)允许eval函式以型别安全的方式撰写。  动态型别使元程序设计更为强大,且更易于使用。例如C++模板的写法,比起等价的Ruby或Python写法要来的麻烦。更高度的执行时期构成物,如元类别(metaclass)和内观(Introspection),对静态型别语言而言通常更为困难。
title: 强型别和弱型别.
content: 强型别的基本定义即为,禁止错误型别的参数继续运算。C语言的型别转换即为缺乏强型别的证例;如果编写者用C语言对一个值转换型别,不仅令编译器允许这个代码,而且在执行时期中也同样允许。这使得C代码可更为紧密和快速,不过也使除错变的更为困难。  部分学者使用术语内存安全语言(或简称为安全语言)形容禁止未定义运算发生的语言。例如,某个内存安全语言将会检查阵列边界。  弱型别意指一个语言可以隐式的转换型别(或直接转型)。看看先前的例子:  在弱型别语言中编写上述代码,并不清楚将会得到哪一种结果。某些语言如Visual Basic,将会产生可以运作的代码,它将会给出的结果是42:系统将字串"37"转换成数字37,以符合运算上的直觉;其它的语言,像JavaScript将会产生的结果是"537":系统将数字5转换成字串"5"并把两者串接起来。在Visual Basic和JavaScript中,最终的型别是以那两个算子为考量的规则所决定。在部分语言中,如AppleScript,某个值最终的型别,只以最左边的算子的型别所决定。  设计精巧的语言也允许语言显现出弱型别(借由类型推断之类的技术)的特性以方便使用,并且保留了强型别语言所提供的型别检查和保护。例子包括VB.Net、C#以及Java。  运算子多载所带来的简化,像是不以算术运算中的加法来使用「+」,可以减少一些由动态型别所造成的混乱。例如,部分语言使用「.」或「&」来串连字串。
title: 型别系统的安全性.
content: 程序语言的型别系统的第三种分类方法,就是型别运算和转换的安全性。如果它不允许导致不正确的情况的运算或转换,电脑科学就认为该语言是「型别安全」的。  再次看看这个假码例子:  在一个如Visual Basic的语言中,例子中的变量z得到的值为42。不管编写者有没有这个意图,该语言定义了明确的结果,且程序不会就此崩溃,或将不明定义的值赋给z。就这方面而言,这样的语言就是型别安全的。  现在来看C的相同例子:  在这个例子中,z将会指向一个超过y位址5个字节的内存位址,相当于指向y字串的指标之后的两个空字符之处。这个位址的内容尚未定义,且有可能超出内存的寻址界线,而且就这么引用参考z会引起程序的终止。虽是一个良好型别,但却不是内存安全的程序——如果以对型别安全语言而言不该发生为先决条件的话。
title: 多态性和型别.
content: 术语「多态性」指的是:代码(尤其是函式和类别)对各种型别的值能够动作,或是相同资料结构的不同实体能够控制不同型别的元素。为了提升复用代码的潜在价值,型别系统逐渐允许多态性:在具有多态性的语言中,程序设计者只需要实作如列表或词典的资料结构一次,而不是对使用到它的元素的每一个型别都规划一次。基于这个原因,电脑学家也称使用了一定的多态性的方法为泛型程序设计。型别理论的多态性基础与抽象化、模组化和(偶尔)子型别有相当密切的联系关系。
title: 推断型别.
content: 推断型别(鸭子类型,Duck typing)最初是由Dave Thomas在Ruby社群中提出的,推断型别用了这个论证法「如果它像什么,而且其它地方也像什么,那么它就是什么。」  在某些程序设计环境中,两个物件可以有相同的型别,即使它们没有什么交集。一个例子是C++在迭代器和指标之间的双重性。两者皆以不甚相同的机制实作并提供一个* 运算。  这个技术之所以常被称作「鸭子型别」,是基于这句格言:「如果它摇摇摆摆的走法很像鸭子,而且它的嘎嘎叫声也像鸭子,那它就是一只鸭子!」
title: 显示宣告和隐式暗示.
content: 许多静态型别系统,如C和Java,要求要宣告型别:编写者必须以指定型别明确地关联到每一个变量上。其它的,如Haskell,则进行型别推断:编译器根据编写者如何运用这些变量,以草拟出关于这个变量的型别的结论。例如,给定一个函式"f(x,y)",它将"x"和"y"加起来,编译器可以推断出"x"和"y"必须是数字——因为加法仅定义给数字。因此,任何在其它地方以非数值型别(如字串或链表)作为参数来呼叫"f"的话,将会发出一个错误。  在代码中数值、字串常数以及运算式,经常可以在详细的前后文中暗示型别。例如,一个运算式codice_2可暗示浮点数型别;而codice_3则可暗示一个整数的链表;通常是一个阵列。
title: 型别的型别.
content: "型别的型别"是一种种类。在型别程序设计中有明确的种类,如Haskell程序语言的"型别建构子",在申请比较简单的型别之后,其返回一个简单的型别。例如,型别建构子"二选一"有这些种类"* -> * -> *"("*"代表种类),而且它的申请"二选一字串整数"是一个简单的型别。然而,大多数程序语言的型别,是由编写者来暗示或"写死",这就并未将"种类"的概念用作为首选层。  型别可分为几个大类:
title: 相容性:等价性和子型别.
content: 对于静态型别语言的型别检查器,必须检验所有运算式的型别,是否与前后文所期望的型别一致。例如指派语句codice_4,推断运算式"e"的型别,必定与宣告或推断的变量型别codice_5一致。这个一致性的概念,就称为"相容性",是每一个程序语言所特有的。  很明显,如果"e"和codice_5的型别相同,就允许指派,然后这是一个有效的运算式。因此在最简单的型别系统中,问题从两个型别是否相容,简化为两个型别是否"相等"(或"等价")。然而不同的语言对于两个型别运算式是否理解为表示了相同型别,有着不同的标准。型别的"相等理论"的差异相当巨大,两个极端的例子是"结构型别系统"(Structural type system),任两个以相同结构所描述的值的型别都是等价的,且在"标明型别系统"(Nominative type system)上,没有两个独特的语法构成的型别运算式表示同一型别,("即"型别若要相等,就必须具有相同的「名字」)。  在子型别的语言中,相容关系更加复杂。特别是如果"A"是"B"的子型别,那么型别"A"的值可用于型别"B"也属意料之中,但反过来就不是这样。如同等价性,对每一个程序语言而言,子型别的关系的定义是不同的,可能存在各种变化。在语言中出现的参数或者特定的多态性,也可能意味着具有对型别的相容性。
title: 争议.
content: 在强型别、静态型别语言的支持者,和动态型别、自由形式的支持者之间,经常发生争执。前者主张,在编译的时候就可以较早发现错误,而且还可增进执行时期的效能。后者主张,使用更加动态的型别系统,分析程序码更为简单,减少出错机会,才能更加轻松快速的编写程序。与此相关的是,考虑到在型别推断的程序语言中,通常不需要手动宣告型别,这部分的额外开销也就自动降低了。

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

Type system: In programming languages, a type system is a logical system comprising a set of rules that assigns a property called a type to the various constructs of a computer program, such as variables, expressions, functions or modules. These types formalize and enforce the otherwise implicit categories the programmer uses for algebraic data types, data structures, or other components (e.g. "string", "array of float", "function returning boolean"). The main purpose of a type system is to reduce possibilities for bugs in computer programs by defining interfaces between different parts of a computer program, and then checking that the parts have been connected in a consistent way. This checking can happen statically (at compile time), dynamically (at run time), or as a combination of both. Type systems have other purposes as well, such as expressing business rules, enabling certain compiler optimizations, allowing for multiple dispatch, providing a form of documentation, etc.