2012-04-04 20 views
5

Tôi đang cố gắng viết một số mã Haskell trong đó có nhiều loại dữ liệu, mỗi loại có thể có nhiều triển khai. Để thực hiện điều này, tôi định nghĩa mỗi kiểu dữ liệu là class có các phương thức là các hàm tạo và các bộ chọn có liên quan, và sau đó thực hiện tất cả các hoạt động trên các thành viên của lớp đó theo các hàm tạo và bộ chọn đã cho.Vui với các loại! Giải quyết nhiều khai báo cá thể

Ví dụ, có lẽ A là một lớp đa thức (với các phương pháp getCoefficientsmakePolynomial) mà có thể có một đại diện như một SparsePoly hoặc một DensePolyB là một lớp số phức tạp (với các phương pháp getReal, getImagmakeComplex) có thể được được thể hiện dưới dạng ComplexCartesian hoặc ComplexPolar.

Tôi đã sao chép một ví dụ tối thiểu bên dưới. Tôi có hai lớp học AB mỗi lớp đều có triển khai. Tôi muốn tự động tạo tất cả các phiên bản của cả hai lớp thành các phiên bản Num (điều này yêu cầu các tiện ích mở rộng loại FlexibleInstancesUndecidableInstances). Đây hoạt động tốt khi tôi chỉ có một trong A hoặc B, nhưng khi tôi cố gắng biên dịch với cả hai, tôi nhận được lỗi sau:

Duplicate instance declarations: 
    instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) => 
         Num (a x) 
    -- Defined at test.hs:13:10-56 
    instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) => 
         Num (b x) 
    -- Defined at test.hs:27:10-56 

Tôi cho rằng thông điệp của tờ khai dụ trùng lặp 'là bởi vì một kiểu dữ liệu có thể được tạo thành một phiên bản của cả hai AB. Tôi muốn có thể hứa với trình biên dịch rằng tôi sẽ không làm điều đó, hoặc có thể chỉ định một lớp mặc định để sử dụng trong trường hợp một kiểu là một cá thể của cả hai lớp.

Có cách nào để thực hiện việc này (có thể là một loại tiện ích mở rộng khác không?) Hoặc đây có phải là điều tôi đang gặp phải không?

Dưới đây là mã của tôi:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-} 

class A a where 
    fa :: a x -> x 
    ga :: x -> a x 

data AImpl x = AImpl x deriving (Eq,Show) 

instance A AImpl where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where 
    a1 + a2 = ga (fa a1 + fa a2) 
    -- other implementations go here 


class B b where 
    fb :: b x -> x 
    gb :: x -> b x 

data BImpl x = BImpl x deriving (Eq,Show) 

instance B BImpl where 
    fb (BImpl x) = x 
    gb x = BImpl x 

instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where 
    -- implementations go here 

Edit: Để làm cho bản thân mình rõ ràng, tôi không cố gắng để viết bất kỳ mã thực tế sử dụng kỹ thuật này. Tôi đang làm nó như một bài tập để giúp bản thân mình hiểu được hệ thống kiểu và phần mở rộng tốt hơn.

+4

Liên quan: [Làm thế nào để viết, "nếu typeclass a, thì a cũng là một thể hiện của b theo định nghĩa này."] (Http://stackoverflow.com/a/3216937/98117). – hammar

Trả lời

11

này một phần của câu hỏi của bạn

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

không đúng. Nó thực sự là vì bạn đã viết hai trường hợp,

instance Num (a x) 
instance Num (b x) 

rằng trình biên dịch không thể nói ngoài (xem liên kết từ bình luận @ Hammar của, bối cảnh lớp học không được tính cho mục đích phân biệt giữa tờ khai chẳng hạn).

Một giải pháp là thêm một loại nhân chứng.

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-} 

data AWitness 

data AImpl witness x = AImpl x deriving (Eq,Show) 

instance A (AImpl AWitness) where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where 
    a1 + a2 = ga (fa a1 + fa a2) 

Trình biên dịch có thể sử dụng các loại nhân chứng để phân biệt giữa các khai báo cá thể của bạn.

+0

Cảm ơn, đây là cách tiếp cận mà tôi đã sử dụng. –

4

Không có cách nào thực sự tốt để thực hiện việc này; thực hành tốt nhất là để xác định một số hằng số như

plusA, minusA :: (A a, Num x) => a x -> a x -> a x 

khiến cho việc soạn các Num trường hợp cơ khí hơn sau khi bạn có một A dụ:

instance A Foo where ... 
instance Num x => Num (Foo x) where 
    (+) = plusA 
    (-) = minusA 
+0

Cảm ơn! Tôi có thể thấy điều này sẽ hữu ích như thế nào nếu bạn chỉ có một số ít triển khai của mỗi lớp. –

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