2012-06-19 26 views
15

Tôi là người mới sử dụng Haskell. Tôi đã nhận thấy rằng Haskell không hỗ trợ tên kỷ lục quá tải:Tại sao Haskell/GHC không hỗ trợ quá tải tên bản ghi

-- 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) 

Khi tôi biên dịch này, tôi nhận được:

[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 

Với "sức mạnh" của hệ thống kiểu Haskell, nó có vẻ như nó phải được dễ dàng cho trình biên dịch để xác định trường nào cần truy cập trong

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

Có một số vấn đề mà tôi không thấy. Tôi biết rằng Báo cáo Haskell không cho phép điều này, nhưng tại sao không?

+1

Đây không phải là câu trả lời cho câu hỏi của bạn, nhưng tôi usuaully chia các loại dữ liệu thành các mô-đun riêng biệt bất cứ khi nào một tình huống như của bạn phát sinh. Ví dụ: tôi có thể tạo mô-đun 'Employee' và mô đun' Manager' và nhập chúng đủ điều kiện như nói 'E' và' M' tương ứng, sau đó sử dụng 'E.firstName',' M.firstName', v.v. Điều này mang lại cho tôi cú pháp hợp lý. (Tôi không nói điều này nhất thiết phải là một ý tưởng hay, nhưng đó là những gì tôi đã làm và nó trở nên độc đáo trong trường hợp của tôi). – gspr

+3

Vâng, nhưng điều này có vẻ như một "kludge" trong một ngôn ngữ khác thanh lịch. – Ralph

Trả lời

9

Hệ thống bản ghi hiện tại không quá phức tạp. Đó là chủ yếu là một số cú pháp đường cho những điều bạn có thể làm với boilerplate nếu không có cú pháp kỷ lục.

Đặc biệt, điều này:

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

tạo (trong số những thứ khác) một hàm firstName :: Employee -> String.

Nếu bạn cũng cho phép trong các mô-đun cùng loại này:

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

sau đó điều gì sẽ là loại của firstName chức năng?

Nó sẽ phải là hai hàm riêng biệt quá tải cùng tên, mà Haskell không cho phép. Trừ khi bạn tưởng tượng rằng điều này sẽ ngầm tạo ra một typeclass và tạo ra trường hợp của nó cho tất cả mọi thứ với một trường có tên là firstName (bị lộn xộn trong trường hợp chung, khi các trường có thể có các loại khác nhau), thì hệ thống hồ sơ hiện tại của Haskell sẽ không có thể hỗ trợ nhiều trường có cùng tên trong cùng một mô-đun. Haskell thậm chí không cố gắng làm bất cứ điều gì như vậy hiện nay.

Tất nhiên, điều đó có thể được thực hiện tốt hơn. Nhưng có một số vấn đề phức tạp để giải quyết, và về cơ bản không có ai đưa ra giải pháp cho họ đã thuyết phục mọi người rằng có một hướng triển vọng nhất để di chuyển.

+0

Tôi đoán bạn có thể tạo ra một typeclass và sau đó có các phương pháp typeclass gọi các phiên bản hồ sơ cụ thể, nhưng điều đó sẽ thêm rất nhiều boilerplate đến một ngôn ngữ mà may mắn thường không cần nó. – Ralph

+1

Nếu bạn cho phép quá tải trường, thì hàm 'firstName' sẽ có loại' forall a. a'. Với kiểu suy luận hoặc kiểu khai báo rõ ràng, kiểu này nên được chuyên biệt hóa. Ghi lại các hàm tạo trong Agda hoạt động như thế này. – JJJ

4

Một tùy chọn để tránh điều này là đặt các loại dữ liệu của bạn trong các mô-đun khác nhau và sử dụng nhập đủ điều kiện. Bằng cách đó, bạn có thể sử dụng cùng một trình truy cập trường trên các bản ghi dữ liệu khác nhau và giữ cho bạn mã sạch sẽ và dễ đọc hơn.

Bạn có thể tạo một module cho người lao động, ví dụ

module Model.Employee where 

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

Và một mô-đun cho quản lý, ví dụ:

module Model.Manager where 

import Model.Employee (Employee) 

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

Và rồi bất cứ nơi nào bạn muốn sử dụng hai loại dữ liệu bạn có thể nhập chúng đủ điều kiện và truy cập chúng như sau:

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 

Hãy nhớ rằng sau tất cả các bạn đang sử dụng hai loại dữ liệu khác nhau và do đó Manger.firstName là một hàm khác so với Employee.firstName, ngay cả khi bạn biết rằng cả hai kiểu dữ liệu đại diện cho một người và mỗi người có một tên. Nhưng tùy thuộc vào bạn, bạn đi đến các kiểu dữ liệu trừu tượng như thế nào, ví dụ để tạo một kiểu dữ liệu Person từ các "bộ sưu tập thuộc tính" đó.

Các vấn đề liên quan