15

我是一个 Haskell 新手。我注意到 Haskell 不支持记录名称重载:

-- Records.hs

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

当我编译这个时,我得到:

[1 of 1] Compiling Main             ( Records.hs, Records.o )

Records.hs:10:5:
    Multiple declarations of `firstName'
    Declared at: Records.hs:4:5
                 Records.hs:10:5

Records.hs:11:5:
    Multiple declarations of `lastName'
    Declared at: Records.hs:5:5
                 Records.hs:11:5

Records.hs:12:5:
    Multiple declarations of `ssn'
    Declared at: Records.hs:6:5
                 Records.hs:12:5

鉴于 Haskell 类型系统的“强度”,编译器似乎应该很容易确定要访问哪个字段

emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]
firstName man
firstName emp

是否有一些我没有看到的问题。我知道 Haskell 报告不允许这样做,但为什么不呢?

4

3 回答 3

16

历史原因。Haskell已经有 许多为更好的记录系统而设计的竞争性设计——事实上如此之多,以至于无法达成共识。然而。

于 2012-06-19T11:56:17.180 回答
9

当前的记录系统不是很复杂。如果没有记录语法,它主要是一些语法糖,可以用于样板文件。

特别是,这:

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

生成(除其他外)一个函数firstName :: Employee -> String

如果您还允许在同一模块中使用此类型:

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

那么firstName函数的类型是什么?

它必须是两个独立的函数重载同名,这是 Haskell 不允许的。除非你想象这会隐式生成一个类型类并为所有具有名为的字段创建实例firstName(在一般情况下会变得混乱,当字段可能具有不同的类型时),否则 Haskell 的当前记录系统将无法支持同一模块中的多个同名字段。Haskell 目前甚至没有尝试做任何这样的事情。

当然,它可以做得更好。但是有一些棘手的问题需要解决,基本上没有人想出解决方案来让每个人相信有一个最有希望的方向前进。

于 2012-06-19T12:18:04.437 回答
4

避免这种情况的一种选择是将您的数据类型放在不同的模块中并使用合格的导入。通过这种方式,您可以在不同的数据记录上使用相同的字段访问器,并使您的代码保持干净和更具可读性。

您可以为员工创建一个模块,例如

module Model.Employee where

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

还有一个用于管理器的模块,例如:

module Model.Manager where

import Model.Employee (Employee)

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

然后无论你想在哪里使用这两种数据类型,你都可以导入它们,并按如下方式访问它们:

import           Model.Employee (Employee)
import qualified Model.Employee as Employee
import           Model.Manager (Manager)
import qualified Model.Manager as Manager

emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]

name1 = Manager.firstName man
name2 = Employee.firstName emp

请记住,毕竟您使用的是两种不同的数据类型,因此 Manger.firstName 是 Employee.firstName 之外的另一个函数,即使您知道这两种数据类型都代表一个人并且每个人都有一个名字。但是,抽象数据类型能走多远取决于您,例如从那些“属性集合”中创建一个 Person 数据类型。

于 2012-07-07T12:50:38.283 回答