什么是Haskell中类型的coproduct(不相交联合)的语法?

时间:2013-01-09 22:06:08

标签: haskell

考虑以下

data Point=Point{x::Float,y::Float}
data Shape=Circle{centre::Point,radius::Float}
           |Rectangle {uleft::Point,bRight::Point}

这里的Shape类型是Circle和Rectangle两种类型的副产品。我可能想在其他地方重用Circle和Rectangle类型。所以这样做会很有用:

data Point=Point{x::Float,y::Float}
data Circle=Circle{centre::Point,radius::Float}
data Rectangle=Rectangle {uleft::Point,bRight::Point}
data Shape =Circle | Rectangle

但是当我这样做时出现编译错误:Circle被声明两次。 尝试这个的正确语法是什么,或者这不可能?

2 个答案:

答案 0 :(得分:14)

Haskell中类型的副产品通常用Either表示:

data Either a b = Left a | Right b

type Shape = Either Circle Rectangle
-- so you have shapes as either Left c for some Circle c
-- or Right r for some Rectangle r

虽然由于技术原因it isn't exactly a coproduct,但效果非常好。另一种常见的方法是定义类似的类型:

data Shape = CircleShape Circle | RectangleShape Rectangle

因此CircleShape :: Circle -> ShapeRectangleShape :: Rectangle -> Shape是您的两次注射。

如你在问题中所说,原来的ShapeCircleRectangle类型的副产品,这是错误的,因为后两者不是类型。如果你想进行设置以使Circle p r既是类型Circle的值又是类型Shape的值,那么这实际上与Haskell类型系统的精神背道而驰(尽管类似的系统扩展可能类似。

答案 1 :(得分:10)

这不是直接可行的,但您有几个选择。在这种情况下,我会使用GADT索引DataKind

{-# LANGUAGE DataKinds, GADTs, KindSignatures #-}

data ShapeType = Circle | Rectangle

data Shape :: ShapeType -> * where
     CircleShape :: { centre :: Point, radius :: Float } -> Shape Circle
     RectangleShape { uleft :: Point, bRight :: Point } -> Shape Rectangle

然后,无论何时你想要处理形状,你只需使用Shape a,如果你想要一个矩形或圆形,你分别使用Shape RectangleShape Circle