`
edge
  • 浏览: 66995 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Scala概述(五)抽象(3)

阅读更多

5.3 用抽象类型建立泛型模型(Modeling Generics with Abstract Types

一种语言里有两套抽象语法体系肯定会让人产生对这种语言复杂性的疑问:能不能就用一种形式化体系来实现?本节当中我们将会展示,函数式的类型抽象机制(也就是泛型)实际上可以通过面向对象的类型抽象机制(也就是抽象类型)来表达。这种表达方式的思路如下所述:

假定一个参数化类型C有一个类型参数t(可以直接推广到多个类型参数的情况),那么这种表达方式有四个关键组成部分:分别是类型自身的定义、类型实例的创建、基类构造子的调用以及这个类的类型实例(type instances)。

1.    类型定义,C的定义可以重写如下:

class C {

type t

/* rest of class */

}

也就是说,C的类型参数可以用其抽象成员来重新定义。如果类型参数有上界或者下界,则可以带到抽象成员的定义上。类型参数的协变性则不带到抽象成员的定义上,参见第4点。

2.    T为参数创建实例的调用:new C[T]可以写成:

new C{ type t=T }

3.    如果C[T]出现在调用基类构造符的场合,则其子类的定义将会进行如下扩充:

type t = T

4.    每一个C[T]形式的类型定义都被扩充为如下的细化形式:

C { type t = T }  如果t被声明为非协变

C { type t <: T } 如果t被声明为协变

C { type t >: T } 如果t被声明为逆协变

 

这种表达方式在一种情况下会有问题:命名冲突。这是因为参数的名称成为了类的成员,可能和其他成员冲突,包括其父类的类型参数转化成的成员。这种冲突可以通过重命名解决,例如给每个类型名称指定一个唯一数字标识。

两种抽象模式之间可以转换,对于一种语言还是有价值的,因为可以降低其内在的概念复杂性。例如,Scala的泛型,实际上就是一种语法糖,完全可以被抽象类型替代掉。既然如此,也许会有人问,这种语法糖有没有必要性?或者说为什么不只用抽象类型呢,这样可以使语法本身简化很多。实际上,Scala中引入泛型有两重意义:首先,手工把泛型转化为成为抽象类型表达形式并不那么简单,不仅会丧失语法的简洁性,而且还可能带来前述的命名冲突等问题。其次,泛型和抽象类型在Scala中一般扮演不同的角色,泛型一般用于类型的实例化,而抽象类型主要用于在调用者代码中对相应的抽象类型进行引用。后者主要来自于两个场合:一个是有人需要在客户代码中隐藏相关类型信息,用于构造类似于SML模式的模块系统。另一个是在子类中协变地继承父类的类型,从而获得族多态。

可能有人会问,那么是否可以反过来用泛型来替代抽象类型呢?一些对于两种抽象方式都支持的系统进行的研究[27]证实,这样做要困难得多,至少整个程序都需要重写。不仅如此,如果系统要实现受限多态的话,重写类型上/下界的部分会呈平方级增长[8]。实际上这一点也不奇怪,因为这两种类型体系的理论基础就不同,泛型(不带F-界的)可以用F<:系统来表达[11],而抽象类型则建立在类型依赖的基础之上。后者比前者的表现力更强,例如,带路径依赖类型的vObj演算是可以涵盖F<:的。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics