我想知道是否通常有可能统一继承和参数多态性("泛型")的概念,特别是关于方差,以及如何("语法")和在哪里(use-site/declaration-site)定义它们?
考虑这个观点:
- 子类型(例如
S <: T
)可以被视为协变行为,因为接受T
的输入参数也将接受S
。 - 将"继承模型的方差"更改为不变只能在定义端通过禁止子类型(例如向类定义添加
final
修饰符)来实现,据我所见,在大多数情况下,反方差是不可能的 - 参数多态性在默认情况下是不变的,但可以设置为共/逆变
两者之间似乎存在不可忽略的概念不匹配,考虑到
- 语言允许"不安全"协方差(例如Java/c#中的
String[] <: Object[]
)所产生的痛苦 - 与继承 相比,继承/参数多态性的声明和使用方式的差异
在某些语言中,两者可以很好地配合使用,比如
class Foo extends Ordered[Foo]
实现排序/比较行为。
- 是否可以想象继承和参数多态性的概念可以统一并获得相同的默认方差行为(例如默认的协方差,或者这会导致需要用不变性注释标记大多数类型,因此只是将丑陋转移到另一个点)?如果数据结构在默认情况下也是不可变的,这样会更实用吗?
- 是否有一个正式的系统证明这是合理的?
- 无论具体的编程语言是什么,哪些语法选项/更改最有可能是必要的?
- 是否有一些工作的例子或语言,这/类似的东西已经在工作?
协方差/逆变性通常是指这个。假设X
、Y
、Z
是类型。进一步假设a → b
表示一个函数类型,参数类型为a
,结果类型为b
。<:
表示子类型关系,或者可能是"一致性"的其他概念。⇒箭头的意思是"需要"。那么以下语句成立:
X <: Y ⇒ (Z → X) <: (Z → Y)
X <: Y ⇒ (Y → Z) <: (X → Z)
也就是说,函数类型构造函数相对于结果类型(数据源)是协变的,相对于参数类型(数据接收器)是逆变的。这是一个基本事实,你或多或少不能做任何太有创意的事情,比如反转箭头的方向。当然,你总是可以用无方差来代替协方差或逆变性(大多数语言都是这样做的)。
对象类型可以用函数类型进行规范编码,因此这里也没有太多的自由。每个类型参数表示数据源(协变)或数据接收器(逆变),或者两者都表示(无变)。如果它在一种语言中是正音和逆变,那么在另一种语言中,它要么是逆变的,要么是不发音的。
我认为Scala在这方面非常接近理想的语言。您引用了一个看起来很像Scala的示例,因此您很可能熟悉该语言。我想知道为什么你认为它的类型系统只在某些情况下工作得很好。还有其他的例子吗?
每个有抱负的语言设计师都应该阅读Luca Cardelli的《A Theory of Objects》。