2011-04-22 32 views
14

Xem ví dụ về mã bên dưới. Nó sẽ không biên dịch. Tôi đã nghĩ rằng có lẽ đó là bởi vì nó phải có một loại duy nhất cho tham số đầu tiên trong hàm kiểm tra. Nhưng điều đó không có ý nghĩa bởi vì nếu tôi không phù hợp với mô hình trên nó để nó sẽ biên dịch, tôi có thể gọi nó với cả hai MyObj11 5MyObj21 5 đó là hai loại khác nhau.Bạn có thể tạo mẫu đối sánh trên một tham số kiểu lớp bị ràng buộc không?

Vì vậy, hạn chế nào để bạn không thể khớp mẫu trên các hàm tạo với thông số ràng buộc loại lớp? Hoặc có một số cơ chế mà bạn có thể?

class SomeClass a where toString :: a -> String 

instance SomeClass MyType1 where toString v = "MyType1" 
instance SomeClass MyType2 where toString v = "MyType2" 

data MyType1 = MyObj11 Int | MyObj12 Int Int 
data MyType2 = MyObj21 Int | MyObj22 Int Int 

test :: SomeClass a => a -> String 
test (MyObj11 x) = "11" 
test (MyObj12 x y) = "12" -- Error here if remove 3rd line: rigid type bound error 
test (MyObj22 x y) = "22" -- Error here about not match MyType1. 
+0

điều này có thể có ý nghĩa nếu các lớp của haskell đã bị đóng - nghĩa là bạn có thể chỉ định hành vi cho mọi triển khai. Nhưng các lớp Haskell được mở - nên 'test' làm gì nếu đưa ra một giá trị kiểu' SomeoneElsesType' khi 'instance SomeClass SomeoneElsesType trong đó toString v =" mwahahahah "'? câu trả lời dons là cách thích hợp để xử lý điều này. – rampion

+3

Câu hỏi đó dường như rõ ràng đối với tôi: nó sẽ làm bất cứ điều gì phù hợp với mô hình cho biết nó nên.Và nếu không có mẫu phù hợp với nó, nó sẽ cung cấp cho một lỗi như giống như bất kỳ tình huống như vậy. Tôi không thấy sự khác biệt. – mentics

Trả lời

19

nó là gì mà hạn chế, do đó bạn không thể mô hình phù hợp về nhà xây dựng với một tham số hạn chế kiểu lớp?

Khi bạn khớp mẫu trên một hàm tạo rõ ràng, bạn cam kết một đại diện kiểu dữ liệu cụ thể. Kiểu dữ liệu này không được chia sẻ giữa tất cả các cá thể của lớp và do đó không thể viết một hàm hoạt động cho tất cả các cá thể theo cách này.

Thay vào đó, bạn cần phải kết hợp các hành vi khác nhau muốn của bạn với mỗi trường hợp, như vậy:

class C a where 
    toString :: a -> String 
    draw  :: a -> String 

instance C MyType1 where 
    toString v = "MyType1" 

    draw (MyObj11 x) = "11" 
    draw (MyObj12 x y) = "12" 

instance C MyType2 where 
    toString v = "MyType2" 

    draw (MyObj22 x y) = "22" 

data MyType1 = MyObj11 Int | MyObj12 Int Int 
data MyType2 = MyObj21 Int | MyObj22 Int Int 

test :: C a => a -> String 
test x = draw x 

Các chi nhánh của gốc test chức năng của bạn bây giờ được phân phối giữa các trường.

Một số thủ thuật thay thế liên quan đến việc sử dụng class-associated data types (nơi bạn chứng minh trình biên dịch là loại dữ liệu được chia sẻ giữa tất cả các trường hợp) hoặc view patterns (cho phép bạn tổng quát mẫu phù hợp).


Xem mẫu

Chúng ta có thể sử dụng xem mẫu để làm sạch các mối liên hệ giữa mô hình kết hợp và kiểu lớp trường hợp, một chút, cho phép chúng ta xấp xỉ mô hình kết hợp giữa các trường hợp bởi mô hình kết hợp trên một chia sẻ kiểu.

Đây là một ví dụ, trong đó chúng tôi viết một hàm, với hai trường hợp, cho phép chúng ta khớp mẫu với bất kỳ thứ gì trong lớp.

{-# LANGUAGE ViewPatterns #-} 

class C a where 
    view  :: a -> View 

data View = One Int 
      | Two Int Int 

data MyType1 = MyObj11 Int | MyObj12 Int Int 

instance C MyType1 where 
    view (MyObj11 n) = One n 
    view (MyObj12 n m) = Two n m 

data MyType2 = MyObj21 Int | MyObj22 Int Int 

instance C MyType2 where 
    view (MyObj21 n) = One n 
    view (MyObj22 n m) = Two n m 

test :: C a => a -> String 
test (view -> One n) = "One " ++ show n 
test (view -> Two n m) = "Two " ++ show n ++ show m 

Lưu ý cách mà cú pháp -> cho phép chúng tôi gọi lại cho đúng view chức năng trong mỗi trường hợp, nhìn lên một mã hóa kiểu dữ liệu tùy chỉnh cho mỗi loại, để phù hợp với mô hình trên đó.

Thiết kế Thách thức là để tìm ra một loại quan điểm cho rằng bắt tất cả các hành vi biến thể bạn quan tâm.

Trong câu hỏi ban đầu của bạn, bạn muốn mỗi nhà xây dựng để có một hành vi khác nhau, do đó, thực sự không có lý do để sử dụng kiểu xem (gửi trực tiếp đến hành vi đó trong từng trường hợp đã hoạt động đủ tốt).

+0

Ok, có vẻ như câu lệnh này là sự khởi đầu của lý do: "Khi bạn khớp mẫu trên một hàm tạo rõ ràng, bạn cam kết với một biểu diễn kiểu dữ liệu cụ thể." Được rồi, vậy tại sao lại như vậy? Tại sao khớp mẫu lại làm cho cam kết này được nhập vào thời điểm đó? – mentics

+1

Cấu trúc kết hợp mẫu * kiểu dữ liệu cụ thể *. Bạn chọn một loại cụ thể và xem xét các nhà xây dựng của nó. Điều đó có nghĩa là mã của bạn không còn hoạt động được cho tất cả các loại. Bây giờ, bạn có thể tưởng tượng một bản dịch của mã trên của tôi, vào cú pháp bạn có cho mẫu khớp với nhiều loại cùng một lúc, nhưng đó không phải là cách Haskell hoạt động. Đó là một ý tưởng thú vị. –

+0

Tôi tin rằng câu hỏi bình luận là, tại sao Haskell không làm việc theo cách đó? Tại sao tính năng này của ngôn ngữ được thiết kế theo cách hạn chế này? –

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