2013-01-09 30 views
5

考虑以下两种类型:如何将haskell字段名称放在不同的名称空间中?

data Point=Point{x::Float,y::Float} 
data Rectangle = {upperLeft::Point, bottomRight::Point} 
data Square = {upperLeft::Point, bottomRight::Point} 

的GHC编译器抱怨说,在与该广场的矩形冲突upperLeft字段名。这看起来很奇怪,因为在它的表面上,每个字段名称应该在类型的名称空间中,否则不能重复使用字段名称,并且我怀疑这会是一种常见的期望。

例如定义我们写了一个变量:

let a=Rectangle{upperLeft=Point 2 3, bottomRight=Point 7 7} 
let a=Square{upperLeft=Point 2 3, bottomRight=Point 7 7} 

从这里我们可以看到,我们应该能够期待每个字段的名称应该是各自类型的命名空间内。

我的用法是否正确或我的期望是否错误?有没有办法解决这个问题?

回答

3

在Haskell中,创建记录类型也会创建存取函数。 例如,您可以在上面定义的变量中的任一变量上运行:x (upperLeft a)以获得2. 这里是summary of record syntax

您的选项要么在记录上使用不同的字段名,要么将它们放在单独的模块中。因为模块都有自己的名称空间,所以如果将Square放在Square模块中并将Rectangle放在Rectangle模块中,则可以重新使用字段名称。

+1

将每种类型放置在其自己的模块中似乎是一个矫枉过正的问题。我意识到accessor函数已经创建,但我希望类型推断能够选择正确的字段名称来使用。 –

+0

是的,我同意在这种情况下分离模块感觉像是矫枉过正。我可能只是改变字段名称。像'rectUpperLeft'这样的东西不比'upperLeft'长。 我同意重复使用字段名称似乎是一个有用的和合理的事情期望,但据我所知,你不能用Haskell的记录做到这一点。 – astrieanna

2

您可以将Square构造函数放在Rectangle类型中,因为它实际上更类似于这种类型的特化。

打字的东西了在GHCI,这似乎很好地工作:

data Point 
    = Point{x::Float,y::Float} 
    deriving (Eq, Show) 

data Rectangle 
    = Rectangle {upperLeft::Point, bottomRight::Point} 
    | Square {upperLeft::Point, bottomRight::Point} 
    deriving (Eq, Show) 

let r = Rectangle (Point 3.0 4.0) (Point 4.0 2.0) 
let s = Square (Point 2.0 4.0) (Point 4.0 2.0) 

然后就可以调用两个:

upperLeft s 
upperLeft r 

虽然你可能想改变广场,因为它有一些限制它必须满足:

data Rectangle 
    = Rectangle {upperLeft::Point, bottomRight::Point} 
    | Square {upperLeft::Point, size::Float} 
    deriving (Eq, Show) 
4

因为可以通过字段名称访问对象,所以co mpiler必须能够从其字段名称推断对象的类型。例如,在

boundingBox x = bottomRight x - upperLeft x 

存取器bottomRightupperLeft被用于推断的x类型。如果允许多种类型具有相同的访问者名称,则不可能推断该类型。

为避免名称冲突,一个常见的约定是在所有字段名称上加上前缀。 GHC项目中使用该惯例。

data Rectangle = {rc_upperLeft :: Point, rc_bottomRight :: Point} 
data Square = {sq_upperLeft :: Point, sq_bottomRight :: Point} 
相关问题