考虑以下
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被声明两次。 尝试这个的正确语法是什么,或者这不可能?
答案 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 -> Shape
和RectangleShape :: Rectangle -> Shape
是您的两次注射。
如你在问题中所说,原来的Shape
是Circle
和Rectangle
类型的副产品,这是错误的,因为后两者不是类型。如果你想进行设置以使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 Rectangle
或Shape Circle