我是一名 C# 开发人员。来自面向对象的世界,我从接口、类和类型层次结构的角度开始思考。由于 Haskell 缺乏面向对象,有时我发现自己陷入困境,无法想出用 Haskell 建模某些问题的方法。
如何在 Haskell 中对涉及类层次结构的现实世界情况进行建模,如下所示:http://www.braindelay.com/danielbray/endangered-object-orient-programming/isHierarchy-4.gif http://www.braindelay.com/danielbray/endangered-object-oriented-programming/isHierarchy-4.gif
首先:标准的面向对象设计在 Haskell 中不能很好地工作。你可以对抗语言并尝试做出类似的东西,但这将是一种挫败感的练习。所以第一步是为你的问题寻找 Haskell 风格的解决方案,而不是寻找在 Haskell 中编写 OOP 风格解决方案的方法.
但这说起来容易做起来难!从哪里开始呢?
因此,让我们拆解 OOP 为我们所做的事情的具体细节,并思考这些细节在 Haskell 中会是什么样子。
-
Objects:粗略地说,对象是一些数据和操作该数据的方法的组合。在 Haskell 中,数据通常使用以下方式构建:代数数据类型;方法可以被认为是将对象的数据作为初始隐式参数的函数.
-
Encapsulation: However, the ability to inspect an object's data is usually limited to its own methods. In Haskell, there are various ways to hide a piece of data, two examples are:
-
在不导出类型构造函数的单独模块中定义数据类型。只有该模块中的函数可以检查或创建该类型的值。这有点类似于
protected
or internal
成员。
-
使用部分应用程序。考虑功能
map
它的论点被翻转了。如果您将其应用于列表Int
s,你会得到一个类型的函数(Int -> b) -> [b]
。从某种意义上说,您提供的列表仍然“在那里”,但除了通过该函数之外,没有其他东西可以使用它。这相当于private
成员,并且部分应用的原始函数与 OOP 风格的构造函数相当。
-
“临时”多态性:通常,在面向对象编程中,我们只关心某个东西实现了一个方法;当我们调用的时候,具体调用的方法是根据实际的类型来确定的。 Haskell 提供类型类用于编译时函数重载,这有多种方式more比 OOP 语言灵活。
-
代码复用:老实说,我的观点是通过继承重用代码过去和现在都是一个错误。在 Ruby 之类的东西中发现的 Mix-ins 让我觉得这是一个更好的 OO 解决方案。无论如何,在任何函数式语言中,标准方法是使用高阶函数分解常见行为,然后专门化通用形式。一个经典的例子是
fold
函数,它概括了几乎所有迭代循环、列表转换和线性递归函数。
-
Interfaces: Depending on how you're using an interface, there are different options:
-
解耦实施:具有类型类约束的多态函数就是您想要的。例如,函数
sort
有类型(Ord a) => [a] -> [a]
;它与您提供的类型的详细信息完全解耦,除了它必须是某种类型实现的列表之外Ord
.
-
通过共享接口处理多种类型:为此,您需要语言扩展存在类型,或者为了简单起见,使用一些变体部分应用如上所述,您可以提前应用函数并处理结果,而不是对其应用值和函数。
-
子类型化,又名“is-a”关系:这是你最不走运的地方。但是,根据多年的专业 C# 开发人员经验来看,在某些情况下,您确实need子类型并不是很常见。相反,请考虑上述内容,以及您试图通过子类型化关系捕获什么行为。
您可能还会发现这篇博文 http://blog.ezyang.com/2010/05/design-patterns-in-haskel/有帮助;它快速总结了在 Haskell 中使用什么来解决 OOP 中经常使用的一些标准设计模式的相同问题。
作为最后的附录,作为一名 C# 程序员,您可能会发现研究它与 Haskell 之间的联系很有趣。相当多负责 C# 的人也是 Haskell 程序员,并且 C# 最近添加的一些内容深受 Haskell 的影响。最值得注意的可能是 LINQ 底层的单子结构,IEnumerable 本质上是列表单子。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)