数据类型中的相同变量名

pau*_*aul 4 haskell

为什么使用数据类型时我不能为这些数据属性提供相同的内部属性名称?

val这里我不能在多个Data中重用变量名

不编译

data Product = Product {val::String}deriving (Show, Eq)
data Price = Price {val::Double}deriving (Show, Eq)
data Discount = Discount { val::Double }deriving (Show, Eq)
Run Code Online (Sandbox Code Playgroud)

编译

data Product = Product {productVal::String}deriving (Show, Eq)
data Price = Price {priceVal::Double}deriving (Show, Eq)
data Discount = Discount { discountVal::Double }deriving (Show, Eq)
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 5

\n

为什么使用数据类型时我不能为这些数据属性提供相同的内部属性名称?

\n
\n\n

如果定义记录类型,则隐式构造了一个“getter”。如果您定义如下记录数据类型:

\n\n
data Product = Product { val :: String } deriving (Show, Eq)\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后 Haskell 将构造一个函数:

\n\n
val :: Product -> String\n
Run Code Online (Sandbox Code Playgroud)\n\n

获取给valProduct对象的 。

\n\n

如果您稍后定义新的记录数据类型:

\n\n
data Price = Price { val :: Double } deriving (Show, Eq)\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么您就定义了 的两个版本val,从而导致名称冲突。

\n\n

DuplicateRecordFields扩展名

\n\n

自 8.0.1 起,Glasgow Haskell 编译器 (GHC) 具有一个扩展,允许DuplicateRecordFields指定具有相同字段名称的两个记录数据类型。

\n\n

使用相同的记录名进行模式匹配是没有问题的,或者当我们构造记录时,例如:

\n\n
productToPrice :: Product -> Price\nproductToPrice (Product {val = x}) = Price { val = 3 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

不会产生任何问题,因为valinProduct { val = x }明确指的是数据构造函数val中定义的Product,而valinPrice { val = 3 }指的是数据构造val函数的Price

\n\n

然而,如果我们将其用作val函数,则会产生歧义:

\n\n
Prelude> val (Product "foo")\n\n<interactive>:15:1: error:\n    Ambiguous occurrence \xe2\x80\x98val\xe2\x80\x99\n    It could refer to either the field \xe2\x80\x98val\xe2\x80\x99,\n                             defined at <interactive>:1:25\n                          or the field \xe2\x80\x98val\xe2\x80\x99, defined at <interactive>:2:21\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们可以添加函数的签名来指定val我们要使用的函数:

\n\n
Prelude> (val :: Product -> String) (Product "foo")\n"foo"\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者通过指定 的类型Product "foo",我们获得相同的效果:

\n\n
Prelude> val (Product "foo" :: Product)\n"foo"\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,考虑到val具有相同的类型,或者具有一些通用含义,最好引入一个类型类,从而val在那里定义该函数。

\n