Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将OOP数据类型重新构造为Haskell类型_Oop_Haskell_Data Structures - Fatal编程技术网

将OOP数据类型重新构造为Haskell类型

将OOP数据类型重新构造为Haskell类型,oop,haskell,data-structures,Oop,Haskell,Data Structures,来自OOP背景的Haskell的类型系统以及数据构造函数和类型类的交互方式很难概念化。我可以理解如何将每一个都用于简单的示例,但事实证明,非常适合OOP风格的数据结构的一些更复杂的示例非常适合转换为类似优雅且可理解的类型 特别是,我在组织如下数据层次结构时遇到了问题 这是一个嵌套很深的层次继承结构,缺乏对子类型的支持,因此不清楚如何在Haskell中将此结构转化为一种自然的感觉替代。可以用sum数据类型替换Polygon之类的内容,声明如下 data Polygon = Quad Point

来自OOP背景的Haskell的类型系统以及数据构造函数和类型类的交互方式很难概念化。我可以理解如何将每一个都用于简单的示例,但事实证明,非常适合OOP风格的数据结构的一些更复杂的示例非常适合转换为类似优雅且可理解的类型

特别是,我在组织如下数据层次结构时遇到了问题

这是一个嵌套很深的层次继承结构,缺乏对子类型的支持,因此不清楚如何在Haskell中将此结构转化为一种自然的感觉替代。可以用sum数据类型替换
Polygon
之类的内容,声明如下

data Polygon
= Quad Point Point
| Triangle Point Point Point
| RegularNGon Int Radius
| ...
但这会丢失一些结构,并且只能对层次结构的一个级别进行令人满意的操作。typeclass可用于实现一种继承和子结构形式,因为
多边形
typeclass可以是
形状
的子类,因此所有
多边形
实例可能都有
质心::点
顶点::[Point]
的实现,但这似乎并不令人满意。在Haskell中捕获图片结构的好方法是什么?

您可以使用sum类型来表示整个层次结构,而不会丢失结构。像这样的东西可以做到:

data Shape = IsPoint Point
           | IsLine Line 
           | IsPolygon Polygon

data Point = Point { x :: Int, y :: Int }

data Line = Line { a :: Point, b :: Point }

data Polygon = IsTriangle Triangle
             | IsQuad Quad
             | ...
等等。基本模式是将每个OO抽象类转换为Haskell sum类型,其每个直接OO子类(可能是抽象的)作为sum类型中的变量。具体的类是产品/记录类型,其中包含实际的数据成员。1

与您习惯的OOP相比,通过这种方式建模,您失去的不是表示层次结构的能力,而是在不接触现有代码的情况下扩展层次结构的能力。和类型是“闭合的”,OO继承是“开放的”。如果您后来决定要为
形状
添加一个
圆圈
选项,则必须将其添加到
形状
,然后在
形状
上模式匹配的任何位置为其添加案例

然而,这种层次结构可能需要在OO中进行相当自由的向下转换。例如,如果您想要一个可以判断两个形状是否相交的函数,那么这可能是
Shape
上的一个抽象方法,就像
Shape.intersects(Shape other)
,因此每个子类型都可以编写自己的实现。但是当我写
Rectangle.intersects(Shape other)
时,如果不知道
Shape
的其他子类在那里,基本上是不可能的。我必须使用
isinstance
检查来查看
other
实际上是什么。但这实际上意味着我可能无法在不重新访问现有代码的情况下添加新的
Circle
子类;需要进行isinstance检查的OO层次结构实际上与Haskell sum类型层次结构一样“封闭”。基本上,通过应用此模式生成的一个和类型上的模式匹配相当于OO版本中的ISinstance和downcasting。仅因为编译器完全了解求和类型(仅可能因为它们是闭合的),如果我在
形状中添加一个
圆圈
大小写
,编译器才能告诉我需要重新访问以处理该情况的所有位置。2

如果你有一个层次结构,它不需要很多向下转换,这意味着各种基类都有它们保证可用的大量有用的接口,你通常通过该接口使用东西,而不是打开它可能是什么,那么你可能可以使用类型类。您仍然需要所有的“叶”数据类型(具有实际数据字段的产品类型),只需为公共接口添加类型类,而不是添加总和类型包装器来对它们进行分组。如果您可以使用这种翻译风格,那么您可以更轻松地添加新的案例(只需添加新的
Circle
数据类型和一个实例,说明它如何实现
Shape
type类;在
Shape
类中任何类型中多态的所有位置现在也将处理
Circle
s)。但是如果你在OO中这样做的话,当你发现你不能处理一般的形状时,你总是可以使用下拉模式作为逃生舱口;哈斯克尔的这种设计是不可能的

但不幸的是,我对“如何在Haskell中表示OO类型层次结构”的“真实”回答是陈腐的:我没有。我在Haskell中的设计与在OO语言中的设计不同,实际上这并不是什么大问题。但是要说我是如何设计这个案例的,我需要更多地了解你使用它们的目的。例如,您可以将形状表示为
Point->Bool
函数(它告诉您是否有任何给定的点在形状内部),并使用
circle::Point->Int->(Point->Bool)
生成与正常形状对应的函数;这种表示法非常适合在不知道任何信息的情况下形成复合相交/并集形状(
intersect-shapeA-shapeB=\point->shapeA-point&&shapeB-point
),但在计算面积和周长等方面却非常糟糕


1如果您有包含数据成员的抽象类,或者您有包含更多子类的具体类,您可以手动将数据成员下推到“叶子”中,将继承的数据成员分解到共享记录中,并使所有“叶子”包含其中一个,拆分一个层,这样您就有了一个包含继承数据成员的产品类型和一个sum类型(该sum类型随后“拆分”为子类的选项),诸如此类

2如果您使用ca
{-# LANGUAGE ExistentialQuantification #-}
...
class Shape a where
    bounds :: a -> AABB
    draw   :: a -> IO ()

data AnyShape = forall a. Shape a => AnyShape a
data Line = Line Point Point
instance Shape Line where ...

data Circle= Circle {center :: Point, radius :: Double}
instance Shape Circle where ...

...
shapes = [AnyShape(Line a b),
          AnyShape(Circle a 3.0),
          AnyShape(Circle b 1.8)]
drawIn box xs = sequence_ [draw s | AnyShape s <- xs, bounds s `hits` box]