Haskell 在超类函数定义中使用子类实现
在我的Haskell程序中,我有一些表示“形状”抽象概念的类型类,即 正如您所看到的,Haskell 在超类函数定义中使用子类实现,haskell,functional-programming,subclass,typeclass,class-hierarchy,Haskell,Functional Programming,Subclass,Typeclass,Class Hierarchy,在我的Haskell程序中,我有一些表示“形状”抽象概念的类型类,即 正如您所看到的,Polygon自然是Shape的一个子类。我还有一些数据类型是这些不同类型类的实例。例如: data Box = Box Point Point Angle instance Shape Box where ... instance Polygon Box where ... --------------------------------- data Circle = Circle Point
Polygon
自然是Shape
的一个子类。我还有一些数据类型是这些不同类型类的实例。例如:
data Box = Box Point Point Angle
instance Shape Box where
...
instance Polygon Box where
...
---------------------------------
data Circle = Circle Point Radius
instance Shape Circle where
...
我有更多可能的形状,例如NGon
,RegularNGon
,等等。我希望能够实现isColliding
,但计算两个形状是否碰撞所需的信息取决于形状
的特定实例的实现。例如,要计算两个长方体是否碰撞,我需要它们的顶点列表。所以我有几个问题:
isColliding
,以便它以特定的方式定义为类型isColliding::(多边形b)=>Box->b->Bool
我对Haskell很陌生,所以如果我的问题措辞不好或者需要任何澄清,请告诉我。您当前的
形状
类说“isColliding
可以通过在另一个形状上使用形状
的方法来判断该形状是否与另一个形状相交”,因为它的签名(形状b)=>a->b->Bool
只告诉您b
有一个Shape
的实例。你是对的,这不是你想要的
您可以使用multipamTypeClasses
来描述两种类型之间的关系:
{-# LANGUAGE MultiParamTypeClasses #-}
class Colliding a b where
collidesWith :: a -> b -> Bool
然后举例说明各种类型的具体组合:
instance Colliding Circle Box where
Circle p r `collidesWith` Box p1 p2 θ = {- … -}
在这里定义实现时,您知道a
和b
的具体类型。这对于您的用例来说可能已经足够好了
但是,如果您有n个类型,那么这将留给您n2个实例。如果您试图像这样定义多态实例,就会遇到问题:
instance (HasBoundingBox b) => Colliding Circle b where
collidesWith = {- … -}
因为这与碰撞圆的所有其他实例重叠:b
将匹配任何类型,并且只添加b
必须具有hasbundingbox
实例的约束。实例解析后,将检查该约束。您可以使用OverlappingInstances
或更新的OVERLAPPABLE
/OVERLAPPING
/Overlapps
杂注来解决这一问题,告诉GHC选择最具体的匹配实例,但如果您只是熟悉Haskell,这可能会比它的价值更麻烦
我得多考虑一下,但肯定有其他的方法。在最简单的情况下,如果您只需要处理几种不同类型的形状,那么您可以将它们设置为单一和类型,而不是单独的数据类型:
data Shape
= Circle Point Radius
| Box Point Point Angle
| …
然后您的isColliding
函数可以是类型Shape->Shape->Bool
,只需在该类型上进行模式匹配即可
一般来说,如果您正在编写一个typeclass,它应该附带实例行为的规则,比如mappend x mempty==mappend mempty x==x
fromData.Monoid
。如果您想不出任何应该始终适用于您的类实例的方程式,那么您应该更喜欢用普通的旧函数和数据类型来表示事物。如果您真的想,您可以有两个函数<在Shape
类中的code>isCollidingShape
和Polygon
类中的isCollidingPolygon
,对于同样是Polygon
的Shape
类中的isCollidingShape=isCollidingPolygon
默认实现。一般来说,这并不是设计接口的正确方法-您已经从接口开始,希望它足以实现特定的功能;相反,您应该首先尝试在某些具体情况下实现该功能,然后将类似的功能抽象到接口中。面向对象继承通常更自然地表示为总和类型。(术语“类”可能会误导——在OOP中,你在分类对象;在Haskell中,你在分类类型。)“面向对象的继承通常更自然地表示为总和类型”。我一直在想人们是从哪里得到这个奇怪的想法的。他们之间毫无共同之处。顺便说一句,对类型的分类自然会导致对对象的分类@莫布尼洛
data Shape
= Circle Point Radius
| Box Point Point Angle
| …