mik*_*era 50 language-agnostic oop polymorphism functional-programming prototype
我目前正在享受从面向对象语言到功能语言的过渡.这是一股清新的空气,我发现自己比以前更富有成效.
但是 - 有一个方面的OOP我还没有在FP方面看到一个令人满意的答案,那就是多态性.即我有大量的数据项,当它们被传递到某些函数时需要以完全不同的方式处理.为了论证,让我们说有多种因素推动多态行为,因此潜在地指数多种不同的行为组合.
在OOP中,可以使用多态性相对较好地处理:通过组合+继承或基于原型的方法.
在FP中我有点卡在:
对于这种情况,推荐的功能方法是什么?还有其他好的选择吗?
mok*_*kus 22
将函数以类似原型的方式放在纯数据结构中 - 这看起来似乎有效,但是它是否也违反了将数据与数据分开定义的想法?
如果虚拟方法调度是您想要解决问题的方式,那么这是一种非常合理的方法.至于将功能与数据分开,这是一个明显无功能的概念.我认为函数式编程的基本原理是函数ARE数据.至于你是否觉得你正在模拟一个虚拟函数,我认为它根本不是模拟.它是一个虚拟功能表,这完全没问题.
仅仅因为语言没有内置的OOP支持并不意味着应用相同的设计原则是不合理的 - 它只是意味着你必须编写更多内置其他语言提供的机器,因为你'重新对抗你正在使用的语言的自然精神.现代类型的函数式语言确实对多态性有非常深刻的支持,但它是一种非常不同的多态方法.
OOP中的多态性很像逻辑中的"存在量化" - 多态值具有某种运行时类型,但您不知道它是什么.在许多函数式编程语言中,多态性更像是"通用量化" - 多态值可以实例化为其用户想要的任何兼容类型.它们是完全相同硬币的两面(特别是,它们根据您是从"内部"还是"外部"查看功能而交换位置),但在设计时却非常困难. "使硬币公平"的语言,特别是在存在其他语言特征的情况下,例如亚型或更高级别的多态性(多态性多态性).
如果它有帮助,您可能想要将函数式语言中的多态性视为非常类似于C#或Java中的"泛型",因为这正是多态性的类型,例如ML和Haskell所支持的.
Lan*_*nbo 13
好吧,在Haskell中,你总是可以创建一个类类来实现一种多态.基本上,它定义了为不同类型处理的函数.示例是类Eq和Show:
data Foo = Bar | Baz
instance Show Foo where
show Bar = 'bar'
show Baz = 'baz'
main = putStrLn $ show Bar
Run Code Online (Sandbox Code Playgroud)
该函数show :: (Show a) => a -> String是为类型类实例的每种数据类型定义的Show.编译器会根据类型为您找到正确的函数.
这允许更一般地定义函数,例如:
compare a b = a < b
Run Code Online (Sandbox Code Playgroud)
将适用于任何类型的类型类Ord.这与OOP不完全相同,但你甚至可以像这样继承类型类:
class (Show a) => Combinator a where
combine :: a -> a -> String
Run Code Online (Sandbox Code Playgroud)
由实例来定义实际函数,您只需定义类型 - 类似于虚函数.
这还不完整,据我所知,许多FP语言都没有类型类.OCaml没有,它将其推到其OOP部分.而Scheme没有任何类型.但是在Haskell中,它是在一定范围内实现某种多态的有效方法.
更进一步,2010标准的新扩展允许类型族等.
希望这对你有所帮助.
Doc*_*own 11
谁说
与数据分开定义纯函数
是最佳做法?
如果你想要多态对象,你需要对象.在功能语言中,可以通过将一组"纯数据"与对该数据进行操作的一组"纯函数"粘合在一起来构造对象.即使没有类的概念,这也可以工作.从这个意义上说,一个类只不过是一段用相同的"纯函数"构造对象的代码.
并且通过用具有相同签名的不同函数替换对象的一些这些函数来构造多态对象.
如果您想了解有关如何使用函数式语言(如Scheme)实现对象的更多信息,请查看本书:
迈克,你的两种方法都是完全可以接受的,正如布朗博士所说,在 SICP 的第 2 章中讨论了每种方法的优缺点。第一个受到某处有一个大类型表的影响,需要维护。第二个是传统的单调度多态性/虚函数表。
该方案没有内置系统的原因是,针对问题使用错误的对象系统会导致各种麻烦,那么如果您是语言设计者,该选择哪个?单分派单继承不能很好地处理“驱动多态行为的多个因素,因此可能呈指数级地许多不同的行为组合”。
总而言之,构建对象的方法有很多种,SICP 中讨论的语言方案只是为您提供了一个基本工具包,您可以从中构建所需的工具包。
在真正的方案程序中,您将手动构建对象系统,然后使用宏隐藏关联的样板。
在 clojure 中,您实际上有一个内置多方法的预构建对象/调度系统,与传统方法相比,它的优势之一是它可以调度所有参数的类型。您(显然)也可以使用层次系统为您提供类似继承的功能,尽管我从未使用过它,因此您应该使用cum grano salis。
但是,如果您需要与语言设计者选择的对象方案不同的东西,您可以只制作一个(或多个)适合的。
这实际上就是您在上面提出的建议。
构建你需要的东西,让它全部工作,用宏隐藏细节。
FP 和 OO 之间的争论不是关于数据抽象是否不好,而是关于数据抽象系统是否是填充程序所有单独关注点的地方。
“我相信一种编程语言应该允许人们定义新的数据类型。我不认为一个程序应该仅仅由新数据类型的定义组成。”