1

我有一个类型类Shape,它声明了所有形状共有的许多函数。其中一个函数 ( refine) 需要返回一个子形状列表。为了表达这个约束,我使用存在量化:

data Shapeable = forall a . Shape a => Shapeable a

并有函数 return [Shapeable]。我有一个额外的限制,即可以优化某些形状(通过refine函数),而其他形状可以检查交叉点(通过intersect函数)。这些是相互排斥的,因为可以优化自身的形状不能检查相交,反之亦然。

如果我不使用量化,我会再创建两个类型类:IntersectableRefineable. 有没有办法在像系统这样的单一类型类中表达不相交的函数集?

4

3 回答 3

5

我建议这样的事情:

data Shape
    = Composite
        { refine :: [Shape]
        , {- other type-class methods go here -}
        }
    | Primitive
        { intersect :: Shape -> Region
        , {- other type-class methods go here -}
        }

...并完全跳过类型类和存在量化。

于 2012-07-17T18:42:53.740 回答
2

我相信你能得到的最接近的是有两个存在的案例:

data Shapeable =
    forall a . (Shape a, Intersectable a) => Intersectable a |
    forall a . (Shape a, Refineable a) => Refineable a
于 2012-07-17T19:24:04.970 回答
1

我建议根本不要使用类型类。将您的操作定义为简单的数据类型:

data ShapeOps a =
    ShapeOps {
      intersect :: Maybe (a -> a),
      refine    :: Maybe (a -> a)
    }

然后你可以使用存在量化:

data Shape =
    forall a. Shape (ShapeOps a) a

这个概念更容易考虑:

data Shape =
    forall a.
    Shape {
      intersect :: Maybe (a -> a),
      refine    :: Maybe (a -> a),
      shape     :: a
    }

使用Maybe只是一个例子。您还可以使用RankNTypes代替存在量化:

newShape ::
    (forall a. (a -> a) -> a -> b) ->
    (forall a. (a -> a) -> a -> b) ->
    ShapeConfig ->
    b

如果它有交集,这个函数可以将形状传递给第一个延续,如果它有细化,则传递给第二个延续。你可以想出各种组合方式。使用Monoid或者Alternative您甚至可以同时执行以下操作:

newShape ::
    (Alternative f) =>
    (forall a. (a -> a) -> a -> f b) ->
    (forall a. (a -> a) -> a -> f b) ->
    ShapeConfig ->
    f b

使用 RankNTypes 的优点是您可以编写更灵活的函数。您现在可以拥有折叠、地图或任何您想要的东西,而不是简单的构造函数。

于 2012-07-17T23:29:17.793 回答