2012-07-17 39 views
1

我有一个类型为Shape的类型声明了许多对所有形状都通用的函数。其中一个函数(refine)需要返回一个子类型列表。为了表达这个约束,我使用存在量化:通过类型系统表达替代功能集

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

,并有函数返回[Shapeable]。我有一个额外的约束条件,可以改进一些形状(通过refine函数),而其他形状可以检查相交(通过intersect函数)。这些是相互排斥的,因为可以自行优化的形状不能检查相交,反之亦然。

如果我没有使用量化,我会创建两个更多的类型:IntersectableRefineable。有没有一种方法可以在单个类型类似系统中表示不相交的函数集?

回答

2

我相信你可以得到最接近的是通过具有两个生存情况:

data Shapeable = 
    forall a . (Shape a, Intersectable a) => Intersectable a | 
    forall a . (Shape a, Refineable a) => Refineable a 
5

我建议是这样的:

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

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

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 

此功能可以在形状传递到第一个延续,如果有交集和第二,如果有细化。你可以想到各种组合方式。使用MonoidAlternative你甚至可以一举两得:

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

使用RankNTypes有你可以写更灵活的功能优势。现在可以使用折叠,地图或任何你想要的东西来代替简单的构造函数。