为什么ADT是好的,而遗传是坏的?
我是一个长期的OO程序员和一个函数式编程新手。从我的小公开代数数据类型对我来说只是一个特殊的继承案例,你只有一个级别的层次结构,超类不能扩展到模块之外。
因此,我(可能是愚蠢的)问题是:如果ADT只是继承的特殊情况(再次这个假设可能是错误的;在这种情况下请纠正我),那么为什么继承会得到所有的批评,而ADT会得到所有的赞扬?
谢谢。
我是一个长期的OO程序员和一个函数式编程新手。从我的小公开代数数据类型对我来说只是一个特殊的继承案例,你只有一个级别的层次结构,超类不能扩展到模块之外。
因此,我(可能是愚蠢的)问题是:如果ADT只是继承的特殊情况(再次这个假设可能是错误的;在这种情况下请纠正我),那么为什么继承会得到所有的批评,而ADT会得到所有的赞扬?
谢谢。
我认为ADT与遗传是互补的。它们都允许您创建可扩展的代码,但扩展性的工作方式是不同的:
面向对象的世界和函数式世界都发展了自己的方式,以允许其他类型的可扩展性。在Haskell中,你可以使用类型类,在ML / OCaml中,人们会使用函数字典或(?)函子来获得固有的扩展性。另一方面,在OOP中,人们使用访问者模式,这本质上是一种获得类似ADT的东西的方法。
OOP 和 FP 中通常的编程模式是不同的,因此当您使用函数式语言进行编程时,您编写代码的方式更频繁地需要函数式扩展性(在 OOP 中类似)。在实践中,我认为拥有一种允许您根据要解决的问题使用两种样式的语言真是太好了。
托马斯·佩特里斯克(Tomas Petricek)的基本面完全正确。你可能还想看看菲尔·瓦德勒(Phil Wadler)关于“表达问题”的文章。
还有另外两个原因,我们中的一些人更喜欢代数数据类型而不是继承:
使用代数数据类型,编译器可以(并且确实)告诉您是否忘记了某个大小写或某个事例是否是多余的。当对事物的操作比对事物的操作多得多时,这种能力特别有用。(例如,比代数数据类型更多的函数,或者比OO构造函数更多的方法。在面向对象的语言中,如果将方法从子类中省略,编译器将无法判断这是一个错误,还是您打算原封不动地继承超类方法。
这个比较主观:很多人都注意到,如果继承使用得当和激进,算法的实现可以很容易地被抹去超过六个类,即使有一个不错的类浏览器,也可能很难遵循程序的逻辑(数据流和控制流)。没有一个不错的类浏览器,你没有机会。如果你想看到一个很好的例子,尝试在Smalltalk中实现bignums,并在溢出时自动故障转移到bignums。这是一个伟大的抽象,但语言使实现难以遵循。在代数数据类型上使用函数,算法的逻辑通常全部在一个地方,或者如果它被拆分,它被拆分成具有易于理解的合约的函数。
附言:你在读什么?我不知道有任何负责人说“ADTs好;哎呀,坏了。