2011-12-02 40 views
5

Tôi muốn xác định kiểu dữ liệu phân cấp, chẳng hạn như:kiểu dữ liệu thứ bậc

data Cat = BigCat | SmallCat 
data Animal = Cat | Dog 

Và sau đó viết một chức năng sẽ đưa động vật như các đối số, và viết nó với mô hình kết hợp như thế này:

bigger::Animal -> Animal -> Bool 
bigger SmallCat BigCat = False 
bigger BigCat SmallCat = True 
bigger Dog Cat = True 
bigger Cat Dog = False 

Trình biên dịch phàn nàn. Nó không muốn khớp với loại Animal được viết rõ ràng trong chữ ký chức năng dựa trên loại Cat trong dòng đầu tiên và thứ hai của mẫu khớp. Tại sao sẽ không thừa nhận rằng một con mèo lớn hoặc con mèo nhỏ một con vật?

+7

"Tại sao sẽ không thừa nhận rằng một con mèo lớn hay con mèo nhỏ là một con vật?" - Bởi vì nó không phải. 'BigCat :: Cat' và' SmallCat :: Cat', nhưng 'Cat :: Animal'. * Constructor * 'Cat' không liên quan đến * type *' Cat'. – delnan

Trả lời

18

Bạn kết hợp các loại với các nhà thầu của chúng tôi. Loại là thứ mà bạn có thể tạo biến. Một hàm tạo kiểu là những gì bạn sử dụng để tạo ra dữ liệu đó. Trong mã của bạn, data Animal = Cat | Dog khai báo loạiAnimal với hai nhà thầu CatDog. Trong dòng khác, bạn xác định một kiểu dữ liệu Cat. Đây không phải là vấn đề vì các kiểu và các hàm tạo không chia sẻ cùng một không gian tên.

Nếu bạn muốn có một đối tượng kiểu Cat nhúng trong bạn Animal (nếu các nhà xây dựng Cat được sử dụng), bạn có thể thêm một trường để các nhà xây dựng:

data Animal = Cat Cat | Dog 

Điều này có nghĩa: "Animal là loại có hai hàm tạo, CatDog. Cat có trường loại CatDog không có. " Nếu bạn muốn tạo các đối tượng với các nhà xây dựng Cat, bạn phải vượt qua một đối tượng kiểu Cat với nó:

myCat = Cat BigCat 

Nếu bạn muốn kết hợp trên một Animal, bạn phải liệt kê tất cả các lĩnh vực của các nhà xây dựng phù hợp . Hãy so sánh một phiên bản chỉnh sửa của mã của bạn:

data Cat = BigCat | SmallCat 
data Animal = Cat Cat | Dog 

bigger :: Animal -> Animal -> Bool 
bigger (Cat SmallCat) (Cat BigCat) = False 
bigger (Cat BigCat) (Cat SmallCat) = True 
bigger Dog   (Cat _)  = True 
bigger (Cat _)   Dog   = False 

Các _ biểu thị một không quan tâm - không phụ thuộc vào đối tượng thông qua, điều này sẽ luôn luôn phù hợp.

+5

+1 cho giải thích về sự khác biệt giữa các loại và nhà thầu thích hợp cho người mới bắt đầu. – luqui

12

Các lỗi ngay lập tức ở đây là Animal được xác định hai cấu trúc dữ liệu, mà không có gì để làm với Cat: Khái niệm Cat là loại Animal, trong khi các biểu hiện BigCat là loại Cat.

Để tạo kiểu dữ liệu lồng nhau trong thời trang đơn giản, bạn sẽ cần phải thực hiện các loại Cat một cuộc tranh cãi với các nhà xây dựng có liên quan:

data Cat = BigCat | SmallCat 
data Animal = Cat Cat | Dog 

Sau đó bạn có thể làm một cái gì đó như thế này:

bigger (Cat SmallCat) (Cat BigCat) = False 
bigger (Cat BigCat) (Cat SmallCat) = True 
bigger Dog (Cat _) = True 
bigger (Cat _) Dog = False 

Điều này trở nên quá vụng về nếu mở rộng vượt ra ngoài một ví dụ tầm thường, tuy nhiên, sự dư thừa trong loại Cat là đau đớn, và hai cách sử dụng khác nhau của mã định danh Cat là không cần thiết gây nhầm lẫn. Một cải tiến nhẹ là để tránh conflating kích thước với loài, và thay vào đó làm một cái gì đó như thế này:

data Size = Big | Small 
data Species = Cat | Dog 
data Animal = Animal Species Size 

Một lợi thế ở đây là bạn có thể dễ dàng mở rộng hoặc loại mà không cần phải thêm càng nhiều vô nghĩa soạn sẵn như nếu không sẽ được yêu cầu . Tuy nhiên, cả hai đều rất ngớ ngẩn như bất cứ điều gì khác hơn là ví dụ đồ chơi và trong thực tế sử dụng có rất nhiều khả năng là một cách tiếp cận tốt hơn nhiều mà sẽ là thích hợp hơn. Nếu các loại thực sự là số đếm đơn giản có ý nghĩa hơn mèo và chó, thì derivingOrd, Enum, & c. thích hợp hơn với các mục đích đặc biệt. Nếu mục đích là một cách thức kết thúc mở rộng hơn của việc mô hình hóa các thực thể với các thuộc tính khác nhau, thì đáng để suy nghĩ về các thiết kế khác phù hợp hơn với vấn đề thực tế.

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