在我的Haskell程序中,我有一些类型表示"形状"的抽象概念,即
-- | Class representing shapes.
class Shape a where
isColliding :: (Shape b) => a -> b -> Bool
centroid :: Point
-- | Class representing shapes composed of a finite number of vertices
and line segments connecting them.
class (Shape a) => Polygon a where
vertices :: a -> Vertices
如您所见,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
,但是计算两个形状是否为碰撞取决于Shape
的特定实例的实现。例如,要计算两个框是否发生碰撞,我需要它们的顶点列表。所以我有几个问题:
isColliding
,以便以特定的方式定义isColliding :: (Polygon b) => Box -> b -> Bool
类型的碰撞?我对Haskell很陌生,所以如果我的问题措辞不当或需要澄清,请告诉我。
答案 0 :(得分:9)
您当前的Shape
班级说“isColliding
可以判断此形状是否与另一个形状相交,只使用另一个形状上的Shape
方法”,因为其签名(Shape b) => a -> b -> Bool
仅告诉您b
的实例为Shape
。所以你说得对,这不是你想要的。
您可以做的一件事是使用MultiParamTypeClasses
来描述两种类型之间的关系:
{-# 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 类型,则会留下 n 2 实例。如果你试图定义像这样的多态实例,你会遇到问题:
instance (HasBoundingBox b) => Colliding Circle b where
collidesWith = {- … -}
因为此与<{1}}的所有其他实例重叠:Colliding Circle
将匹配任何类型,并且只添加{{{ 1}}必须有b
的实例。在实例解析后检查该约束。您可以使用b
或较新的HasBoundingBox
/ OverlappingInstances
/ OVERLAPPABLE
pragma解决此问题,以告知GHC选择最具体的匹配实例,但如果你刚刚熟悉Haskell,这可能比它的价值更麻烦。
我不得不考虑更多,但肯定有其他方法。在最简单的情况下,如果您只需要处理几种不同的形状,那么您可以将它们设为单个和类型而不是单独的数据类型:
OVERLAPPING
然后,您的OVERLAPS
函数可以是data Shape
= Circle Point Radius
| Box Point Point Angle
| …
类型,只是在此类型上匹配模式。
一般来说,如果您正在编写类型类,那么它应该附带 law ,以了解实例的行为方式,例如来自isColliding
的{{1}}。如果你不能想到任何应该总是适用于你的类实例的方程式,你应该更喜欢使用普通的旧函数和数据类型来表示事物。