ADA类型系统(类型兼容性和派生)
所有计算机编程语言从本质上说都是综合考虑其执行环境的因素,旨在找到其问题域的对象和行为的最佳描述规则;其关于问题域的处理某种程度上说是一种抽象的建模过程。
如前所述,ADA的类型系统是这个语言中最为复杂的内容之一。而类型系统很大程度上表现了一个语言对问题域中对象的一种哲学态度。ADA差不多将强类型走到一个极端,体现了它的一种态度(这中强类型可能已经强到了差不多不需要反射Reflection这样的机制了,至少在其基本类型范围内)
本文将讨论ADA的各种数据类型,其间关系,以及在泛型中这些数据类型的表述。
以下是一个ADA各种类型的派生关系图。走一遍这个树状结构可以发现这个设计思路还是有相当合理性的。
注:其中Access类似指针,Array是(逗号分隔成多维)数组结构,Record是复合结构(可以用来表示任何C/C++的struct或class表示的组合数据),Task/Protected是ADA语言内建多任务并发的任务单元。
图片来源: http://en.wikibooks.org/wiki/Ada_Programming/Type_System
而数据类型中一个比较复杂的地方是这个框里的数值类型的关系,它们作为数组下标界限的定义对数组的作用。
ADA的一个比较强大的地方是所有这些类型都具有衍生属性,所以通常开发者遇到一个整数数据类型,很可能它是派生出来的一个类型,而不像多数其他语言那样是一个完全确定的唯一独立类型。这种具体派生关系不像这个图那么简单了。这种衍生从概念上是硬件无关的,而事实上ADA语言有考虑到并又通常将其和硬件相联系。以下对这种类型体系进行分析阐述。
Numeric scalar类型的基本定义,可以用range指定其上下界实现,如下例中的integer_1。但按照ADA语言的观点,这样的定义,实际上是定义了一个匿名类型(从某种程度上就是range 1..10),学理上说它叫做“type of integer_1”,并派生一个subtype名叫integer_1。
一般每个scalar类型都有一个'Base属性,它指代一个类型的“基类型”,我理解可以是承载类型。从概念角度,可以大致理解为编译器指派的能与之兼容(关于兼容性后面马上讨论)的最大类型;从系统和硬件角度看是用来承载这个数据的一个单元(从节省存储角度来看是最小的那种)所对应的类型。每个scalar类型都有一个'First和'Last属性,分别对应其下界和上界。
1 procedure main is 2 type integer_1 is range 1..10; -- definition of a scalar type 3 begin 4 put_line(integer_1'Image(integer_1'Last)); -- result: 10 5 put_line(integer_1'Base'Image(integer_1'Base'Last)); -- result : 127 6 put_line(integer'Image(integer'Last)); -- can be Integer'Base'Image(..); result : 2147483647 (32 bit signed integer) 7 put_line(integer'Base'Image(integer'Base'Last)); -- result: 2147483647 (32 bit signed integer) 8 end main;
类型的派生有两个路径,subtype子类型和new新类型。
subtype的意图是建立一个与被派生的类相互完全兼容的类型。这些类型和'Base类型同属于一个兼容类型族。'Base类型是基底类型(或支撑类型),从逻辑概念上讲,它是这个类型族中最大界限的类型;从硬件和系统角度讲着和分配给它的数据存储有关。而对于Enumeration类型而言,其'Base则是由最早定义的那个界限确定。Enumeration的派生必须是取连续子集。
例如上面说到的内部整数类型的派生性,很可能是这样的。如此这些都是相互兼容的,而用户另行仿照这个用type定义出来的则和它们不相兼容。
1 type Integer is range -2147483648..2147483647; 2 subtype Natural is Integer range 0..Integer'Last; 3 subtype Positive is Integer range 1..Integer'Last;
new type 是基于一个旧族的subtype实例通过给出一个新的类型族的一个subtype,其界限在其界限定义中给定。这个新类型族的基底的规模和旧族一致。new type有两个特点:
1. 它的首个subtype的界限定义必须是所基于的subtype的界限的子集。
2. 它和它的兼容subtype能够使用定义在其父类型上的基本操作。这里所谓基本操作是定义在和父类一起的package中(子package也不行),而且它本身看不见new type。同时运行时界限不出问题需要程序员自行保证。
从定义出发,本质上讲在用的ADA类型都是subtype。而type是制作出一个新兼容族的第一个派生类。位于type的is之后的内容是用来构造这个兼容族的类型描述信息+构造这第一个subtype的界限信息,它是一个匿名类型(就像变量的冒号之后指定的匿名类型,由此类型建立的变量就不能与其他类型兼容了)。
ADA对subtype有以下几种对其类型的分类:
规定(Definite)的和未定的(Indefinite)
规定的是指,由于界限信息可以明确,能用来构造一个数据实体的,而不需要依赖一个赋值的;而未定则需要依赖赋值。通常一些函数参数需要用未定类型以应对不同界限的实例。
有限制(constrained)和未限制(unconstrained)
有限制是指定义中已经将数据的界限定死(其必是规定的类型);未限制是指未限定死,是“含参的”,一般是“未定”类型,当然如果它有默认参数,它就能是“规定的”
显式转换
1 a := SomeType(b); -- SomeType is compatible with th e subtype of a 2 proc(SomeType(b)); -- SomeType is compatible with the subtype of the parameter to proc
重载
兼容的类型无法区分函数重载(它们相当于’相同‘的类型)。
对于常量在不兼容类族之间混淆的,需要用以下格式进行类型指定:
proc(SomeType'(b));
【参考资料】
[1] Ada Programming Type System from wikibooks http://en.wikibooks.org/wiki/Ada_Programming/Type_System