13

考虑以下

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 被声明了两次。尝试这个的正确语法是什么,或者这不可能?

4

2 回答 2

17

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

这很好用,尽管由于技术原因它不完全是 coproduct。另一种常见的方法是定义一个像这样的类型:

data Shape = CircleShape Circle | RectangleShape Rectangle

所以这就是你CircleShape :: Circle -> ShapeRectangleShape :: Rectangle -> Shape两次注射。

正如您在问题中所做的那样,说原始Shape是类型Circle和的副产品是错误的Rectangle,因为后两者不是类型。如果你想设置Circle p r既是 type 的值又是 typeCircle的值Shape,那么这真的与 Haskell 类型系统的精神背道而驰(尽管有足够多的类型系统扩展可能会发生类似的事情)。

于 2013-01-10T01:20:46.430 回答
10

这不是直接可能的,但是您有几个选择。在这种情况下,我将使用 aGADT索引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 Rectangleor Shape Circle

于 2013-01-09T22:30:41.260 回答