为什么要避免使用子类型?

mis*_*tor 34 oop programming-languages functional-programming scala

我看到Scala社区中有很多人建议避免像"瘟疫一样"进行分类.使用子类型的各种原因是什么?有哪些替代方案?

She*_*III 14

类型确定组合物的粒度,即可延伸性.

例如,一个接口,例如Comparable,它结合(因此混淆)相等和关系运算符.因此,不可能仅仅构成一个相等或关系的接口.

一般来说,继承的替代原则是不可判定的.Russell的悖论意味着任何可扩展的集合(即不枚举每个可能的成员或子类型的类型)都可以包含自身,即它本身的子类型.但是为了识别(决定)什么是子类型而不是自身,必须完全枚举其自身的不变量,因此它不再是可扩展的.这是一种悖论,即子类型的可扩展性使继承变得不可判定.这个悖论必须存在,否则知识将是静态的,因此不存在知识形成.

函数组合是子类型的满射替换,因为函数的输入可以替换其输出,即任何预期输出类型的位置,输入类型可以通过将其包装在函数调用中来替换.但是组合不会使子类型的双射合约 - 访问函数输出的接口,不访问函数的输入实例.

因此,组合物不必保持未来(即无界)不变量,因此可以是可扩展的和可判定的.分型可以MUCH更强大的它可证明是可判定的,因为它保持这种双射合同,如进行排序的超类型的不可变列表功能,可以亚型的不可变列表进行操作.

因此,结论是枚举每种类型的所有不变量(即其接口),使这些类型正交(最大化组合的粒度),然后使用函数组合来完成那些不变量不正交的扩展.因此,子类型仅适用于其可证明地模拟超类型接口的不变量的情况,并且子类型的附加接口可证明地与超类型接口的不变量正交.因此,接口的不变量应该是正交的.

类别理论每个子类型的不变量模型提供规则,即Functor,Applicative和Monad,它们保留了提升类型的函数组合,即参见前面提到的列表子类型功能的例子.

  • [最近的想法](http://groups.google.com/group/scala-language/browse_thread/thread/3c886611e320f8cb). (2认同)

mic*_*hid 9

一个原因是当涉及子类型时,equals()很难正确.请参见如何在Java中编写等式方法.特别是"陷阱#4:未能将等于定义为等价关系".实质上:要在子类型下获得平等,您需要双重调度.


Von*_*onC 5

我认为一般情况是语言尽可能"纯粹"(即使用尽可能多的纯函数),并且来自与Haskell的比较.
来自" 程序员的反刍 "

作为混合OO-FP语言的Scala必须处理子类型(Haskell没有)等问题.

正如本PSE答案所述:

无法限制子类型,使其不能超过它继承的类型.
例如,如果基类是不可变的并且定义了一个纯方法foo(...),则派生类不能是可变的,或者foo()使用不纯的函数覆盖

但实际的建议是使用适合您当前正在开发的程序的最佳解决方案.