是否可以统一继承和参数多态的概念?
我想知道是否通常可以统一继承和参数多态性(“泛型”)的概念,特别是关于方差,而且还包括它们必须如何(“语法”)和何处(使用站点/声明站点)被定义?
考虑这个观点:
- 子类型例如
S <: T
可以被视为协变行为,因为接受T
的输入参数也将接受S
。 - 将“继承模型的方差”更改为不变性只能在定义端通过禁止子类型(例如在类定义中添加
final
修饰符)来实现,反方差目前是不可能的正如我在大多数情况下所看到的, - 参数多态性默认情况下是不变的,但可以使其成为同变/逆变,
,两者之间似乎存在不可忽略的概念不匹配
- 考虑到语言通过允许而产生的痛苦 “不安全”协变(例如 Java/C# 中的
String[] <: Object[]
) 与 - 继承相比,声明和使用继承/参数多态性的差异
在某些语言中,可以看出不过,两者可以很好地协同工作,例如
class Foo extends Ordered[Foo]
实现排序/比较行为。
- 继承和参数多态性的概念是否可以统一并获得相同的默认方差行为(例如默认的协方差,或者这会导致需要用不变性注释来标记大多数类型,因此只需将丑陋转移到另一点)?如果数据结构默认情况下也变得不可变,这会更实用吗?
- 是否有一个正式的系统已被证明是合理的?
- 无论具体的编程语言如何,哪些语法选项/更改最有可能是必要的?
- 是否有一些工作示例或某种语言已经可以工作了?
I wonder if it is generally possible to unify the concepts of inheritance and parametric polymorphism ("generics"), especially regarding variance but also in terms how ("syntax") and where (use-site/declaration-site) they would have to be defined?
Consider this point of view:
- Sub-typing e. g.
S <: T
can be perceived as co-variant behavior, because input arguments acceptingT
will also acceptS
. - Changing the "variance of the inheritance model" to invariant is only possible at definition-side by disallowing sub-typing (e. g. adding a
final
modifier to a class definition), contra-variance is not possible as far as I have seen in most cases - Parametric polymorphism is invariant by default, but can be made co-/contra-variant
There seems to be a non-negligible concept mismatch between both, considering
- the pains languages have generated by allowing "unsafe" covariance (e. g.
String[] <: Object[]
in Java/C#) - the differences in how inheritance/parametric polymorphism is declared and used compared to inheritance
In some languages it can be seen that both work together nicely though, like
class Foo extends Ordered[Foo]
to implement ordering/comparison behaviour.
- Is it conceivable that the concepts of inheritance and parametric polymorphism could be unified and gain the same default variance behavior (e. g. covariance by default or would that cause the necessity to mark most types with an invariance annotation instead, therefore just moving the ugliness to another point)? Would this be more practical as if data structures would become immutable by default, too?
- Is there a formal system in which this has been proven to be sound?
- Which syntax options/changes would be most likely necessary, regardless of a concrete programming language?
- Is there some working example or a language where this/something similar is already working?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
协变/逆变通常就是指这一点。假设
X
、Y
、Z
是类型。进一步假设a → b
表示一个函数类型,其参数为a
类型,结果为b
类型。<:
表示子类型关系,或者可能是“一致性”的其他概念。 ⇒ 箭头表示“需要”。那么以下内容成立:即,函数类型构造函数相对于结果类型(数据源)是协变的,并且相对于参数类型(数据接收器)是逆变的。这是一个基本事实,你或多或少不能对此做任何太有创意的事情,比如反转箭头的方向。当然,您始终可以使用无方差来代替协变或逆变(大多数语言都是如此)。
对象类型可以使用函数类型进行规范编码,因此这里也没有太多自由。每个类型参数代表数据源(协变)或数据接收器(逆变)或两者(协变)。如果它在一种语言中是健全且逆变的,那么在另一种语言中它要么是逆变的,要么是不健全的。
我认为 Scala 在这方面非常接近理想的语言。您引用了一个看起来很像 Scala 的示例,因此您很可能熟悉该语言。我想知道为什么您认为它的类型系统仅在某些情况下才能正常工作。其他情况又是怎样的呢?
每个有抱负的语言设计师都应该阅读的一本理论著作是 Luca Cardelli 的《对象理论》。
By covariance/contravariance, one usually means this. Suppose
X
,Y
,Z
are types. Suppose further thata → b
denotes a function type with an argument of typea
and a result of typeb
.<:
denotes the subtype relation, or perhaps some other notion of "conformance". The ⇒ arrow reads "entails". Then the following holds:That is, the function type constructor is covariant with respect to the result type (data source), and contravariant with respect to the argument type (data sink). This is a basic fact and you more or less cannot do anything too creative about it, like reversing the directions of arrows. Of course you can always use no-variance in place of co- or contravariance (most languages do).
Object types can be canonically encoded with function types, so there's not too much freedom here either. Every type parameter represents either data source (covariant) or data sink (contravariant) or both (novariant). If it's sound and contravariant in one language, then in another language it's going to be either contravariant or unsound.
I think Scala is pretty close to an ideal language in this respect. You cite an example that looks a lot like Scala, so you are most likely familiar with the language. I wonder why you think that its type system works nicely only in some instances. What are the other instances?
One theoretical work that every aspiring language designer should read is "A Theory of Objects" by Luca Cardelli.