2012-06-19 24 views
15

我是Haskell的新手。我注意到,哈斯克爾不支持記錄名稱重載:爲什麼Haskell/GHC支持記錄名稱重載

-- 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報告不允許這樣做,但爲什麼不呢?

+1

這完全不是您的問題的答案,但當您的情況出現時,我會將數據類型分割成單獨的模塊。例如,我可能會創建一個Employee模塊和一個Manager模塊,並分別將它們導入爲'E'和'M',然後使用'E.firstName','M.firstName'等這給了我相當好的語法。 (我並不是說這一定是個好主意,但是這就是我最終做的事情,在我的案例中它變得很好)。 – gspr

+3

是的,但這看起來像一個在其他優雅的語言「kludge」。 – Ralph

回答

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當前的記錄系統不會是能夠在同一模塊中支持具有相同名稱的多個字段。哈斯克爾目前甚至沒有試圖做任何這樣的事情。

它當然可以做得更好。但是有一些棘手的問題需要解決,實質上沒有人提出解決方案,讓每個人都確信,目前還有一個最有希望的方向。

+0

我想你可以創建一個typeclass,然後讓typeclass方法調用特定的記錄版本,但是這會給很多通常不需要它的語言添加許多樣板文件。 – Ralph

+1

如果你允許字段重載,那麼'firstName'函數應該有'forall a'類型。了'。對於類型推斷或顯式類型聲明,這種類型應該是專用的。在Agda中記錄構造函數是這樣工作的。 – JJJ

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數據類型。