考虑以下两种类型:
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}
由此我们可以看出,我们应该能够期望每个字段名称应该在它们各自的类型名称空间内。
我的用法是正确还是我的期望错了?有没有办法解决这个问题?
答案 0 :(得分:4)
由于可以通过字段名称访问对象,因此编译器必须能够从其字段名称中推断出对象的类型。例如,在
中boundingBox x = bottomRight x - upperLeft x
访问者bottomRight
和upperLeft
用于推断x
的类型。如果允许多个类型具有相同的访问者名称,则无法推断出类型。
为避免名称冲突,常见的惯例是在所有字段名称上添加前缀。该惯例用于GHC项目。
data Rectangle = {rc_upperLeft :: Point, rc_bottomRight :: Point}
data Square = {sq_upperLeft :: Point, sq_bottomRight :: Point}
答案 1 :(得分:3)
在Haskell中,创建记录类型也会创建访问器功能。
例如,您可以对上面定义的任一变量运行:x (upperLeft a)
以获得2。
这是一个summary of record syntax。
您可以选择在记录上使用不同的字段名称,也可以将它们放在单独的模块中。因为模块每个都有自己的名称空间,如果将Square放在Square模块中,将Rectangle放在Rectangle模块中,则可以重用字段名称。
答案 2 :(得分: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)