Kib*_*rim 16 oop haskell functional-programming
我是C#开发人员.来自世界的OO方面,我首先考虑接口,类和类型层次结构.由于Haskell中缺少OO,有时我发现自己陷入困境,我无法想到用Haskell模拟某些问题的方法.
如何在Haskell中建模涉及类层次结构的真实世界情况,如下所示:http://www.braindelay.com/danielbray/endangered-object-oriented-programming/isHierarchy-4.gif
C. *_*ann 32
首先:标准OO设计在Haskell中不能很好地工作.你可以对抗这种语言并尝试制作类似的东西,但这将是一种令人沮丧的练习.因此,第一步是为您的问题寻找Haskell风格的解决方案,而不是寻找在Haskell中编写OOP风格解决方案的方法.
但说起来容易做起来难!哪里开始?
所以,让我们拆开OOP为我们做的事情的细节,并思考这些在Haskell中的表现.
protected
或internal
成员.map
及其参数被翻转.如果将它应用于Int
s 列表,您将获得类型函数(Int -> b) -> [b]
.从某种意义上说,你给它的列表仍然是"那里",但除了通过函数之外没有别的东西可以使用它.这与private
成员相当,并且部分应用的原始函数与OOP样式的构造函数相当.fold
函数,它概括了几乎所有迭代循环,列表转换和线性递归函数.sort
有类型(Ord a) => [a] -> [a]
; 除了它必须是某种类型实现的列表之外,它与您提供的类型的细节完全分离Ord
.您可能还会发现此博文有用; 它简要概述了您在Haskell中使用的内容,以解决一些标准设计模式在OOP中经常使用的相同问题.
作为最终的附录,作为C#程序员,您可能会发现研究它与Haskell之间的联系很有意思.很多负责C#的人也是Haskell程序员,最近C#的一些新增内容深受Haskell的影响.最值得注意的可能是LINQ下的monadic结构,IEnumerable本质上是list monad.
sep*_*p2k 13
让我们假设以下操作:人类可以说话,狗可以吠叫,如果物种的性别相反,物种的所有成员都可以与同一物种的成员交配.我会像这样在haskell中定义它:
data Gender = Male | Female deriving Eq
class Species s where
gender :: s -> Gender
-- Returns true if s1 and s2 can conceive offspring
matable :: Species a => a -> a -> Bool
matable s1 s2 = gender s1 /= gender s2
data Human = Man | Woman
data Canine = Dog | Bitch
instance Species Human where
gender Man = Male
gender Woman = Female
instance Species Canine where
gender Dog = Male
gender Bitch = Female
bark Dog = "woof"
bark Bitch = "wow"
speak Man s = "The man says " ++ s
speak Woman s = "The woman says " ++ s
Run Code Online (Sandbox Code Playgroud)
现在操作matable
有类型Species s => s -> s -> Bool
,bark
有类型Canine -> String
和speak
类型Human -> String -> String
.
我不知道这是否有帮助,但考虑到问题的相当抽象性,这是我能想到的最好的.
编辑:回应丹尼尔的评论:
集合的简单层次结构可能如下所示(忽略现有的类,如Foldable和Functor):
class Foldable f where
fold :: (a -> b -> a) -> a -> f b -> a
class Foldable m => Collection m where
cmap :: (a -> b) -> m a -> m b
cfilter :: (a -> Bool) -> m a -> m a
class Indexable i where
atIndex :: i a -> Int -> a
instance Foldable [] where
fold = foldl
instance Collection [] where
cmap = map
cfilter = filter
instance Indexable [] where
atIndex = (!!)
sumOfEvenElements :: (Integral a, Collection c) => c a -> a
sumOfEvenElements c = fold (+) 0 (cfilter even c)
Run Code Online (Sandbox Code Playgroud)
现在sumOfEvenElements采用任何类型的积分集合,并返回该集合的所有偶数元素的总和.
Haskell使用抽象数据类型而不是类和对象.这些是关于组织构建和观察信息的方式的问题的两个兼容的观点.我所知道的关于这个主题的最好的帮助是William Cook的论文面向对象编程与抽象数据类型.他对这种效果有一些非常明确的解释
在基于类的系统中,代码是围绕构造抽象的不同方式组织的.通常,构造抽象的每种不同方式都被赋予其自己的类.这些方法只知道如何观察该结构的属性.
在基于ADT的系统(如Haskell)中,代码围绕观察抽象的不同方式进行组织.通常,观察抽象的每种不同方式都被赋予其自己的功能.该函数知道可以构造抽象的所有方法,并且它知道如何观察单个属性,但是知道任何构造.
Cook的论文将向您展示一个很好的抽象矩阵布局,并教您如何组织任何类作为ADY,反之亦然.
类层次结构涉及另外一个元素:通过继承重用实现.在Haskell中,这种重用是通过第一类函数实现的:Primate
抽象中的函数是一个值,Human
抽象的实现可以重用抽象的任何函数Primate
,可以包装它们来修改它们的结果,等等.
设计与类层次结构和设计与抽象数据类型之间并不完全吻合.如果你试图从一个音译到另一个,你将会遇到一些尴尬而不是惯用的东西 - 就像用Java编写的FORTRAN程序.但是,如果您了解类层次结构的原则和抽象数据类型的原则,您可以在一种风格中解决问题,并为另一种风格的同一问题制定合理的惯用解决方案.它确实需要练习.
附录:也可以使用Haskell的类型系统来尝试模拟类层次结构,但这是一个不同的鱼类.类型类与普通类相似,许多标准示例都有效,但它们不同,可能会有一些非常大的意外和不适应.虽然类型类是Haskell程序员的宝贵工具,但我建议任何学习Haskell的人都要学习使用抽象数据类型设计程序.