5

考虑以下两种类型:

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

ghc 编译器抱怨 Rectangle 中的 upperLeft 字段名称与 Square 的名称冲突。这看起来很奇怪,因为从表面上看,每个字段名都应该在类型的命名空间中,否则不能重用字段名,我怀疑这将是一个足够普遍的期望。

例如,要定义一个变量,我们编写:

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

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

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

4

3 回答 3

4

由于可以通过其字段名称访问对象,因此编译器必须能够从其字段名称推断对象的类型。例如,在

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}
于 2013-01-10T21:04:33.347 回答
3

在 Haskell 中,创建记录类型也会创建访问器函数。例如,您可以x (upperLeft a)在上面定义的任一变量上运行:以获得 2。这是记录语法的摘要

您可以选择在记录中使用不同的字段名称,或者将它们放在单独的模块中。因为每个模块都有自己的名称空间,如果将 Square 放在 Square 模块中,将 Rectangle 放在 Rectangle 模块中,您将能够重用字段名称。

于 2013-01-09T22:54:06.407 回答
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

尽管您可能想要更改 Square,因为它必须满足一些限制:

data Rectangle
    = Rectangle {upperLeft::Point, bottomRight::Point}
    | Square    {upperLeft::Point, size::Float}
    deriving (Eq, Show)
于 2013-01-10T18:52:27.853 回答