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
Haskell 在超类函数定义中使用子类实现_Haskell_Functional Programming_Subclass_Typeclass_Class Hierarchy - Fatal编程技术网

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

在我的Haskell程序中,我有一些表示“形状”抽象概念的类型类,即

正如您所看到的,
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
    from
    Data.Monoid
    。如果您想不出任何应该始终适用于您的类实例的方程式,那么您应该更喜欢用普通的旧函数和数据类型来表示事物。

    如果您真的想,您可以有两个函数<在
    Shape
    类中的code>isCollidingShape
    Polygon
    类中的
    isCollidingPolygon
    ,对于同样是
    Polygon
    Shape
    类中的
    isCollidingShape=isCollidingPolygon
    默认实现。一般来说,这并不是设计接口的正确方法-您已经从接口开始,希望它足以实现特定的功能;相反,您应该首先尝试在某些具体情况下实现该功能,然后将类似的功能抽象到接口中。面向对象继承通常更自然地表示为总和类型。(术语“类”可能会误导——在OOP中,你在分类对象;在Haskell中,你在分类类型。)“面向对象的继承通常更自然地表示为总和类型”。我一直在想人们是从哪里得到这个奇怪的想法的。他们之间毫无共同之处。顺便说一句,对类型的分类自然会导致对对象的分类@莫布尼洛
    data Shape
      = Circle Point Radius
      | Box Point Point Angle
      | …