2012-02-15 27 views
10

Tôi có một loạt các hàm hoạt động trên Vectơ, tức là các danh sách có độ dài được thực thi kiểu.Khai báo các thể hiện của các từ đồng nghĩa loại tham số

Tôi đang cố gắng để làm cho loại của tôi dễ dàng hơn để viết, tức là thay vì viết

foo :: (Fold Integer v, Map Integer Integer v v, ...) => ... 

Tôi đang khai báo một lớp mới NList vì vậy tôi chỉ có thể viết foo :: NList v Integer => ...

The (giản thể) lớp trông như thế này:

class (Fold (v i) i 
     , Map i i (v i) (v i) 
     , Map i (Maybe i) (v i) (v (Maybe i)) 
    ) => NList v i 

như bạn thấy, tôi phải giữ "vector" kiểu tách biệt với "mục" loại (tức là v riêng biệt từ i) để tôi có thể làm những việc như Map lên một vector Maybe.

Như vậy, v phải có loại * -> *i loại *.

Tuy nhiên, khi tôi cố gắng nhanh chóng nó với vectơ như vậy:

instance NList Vec2 Integer 
instance NList Vec3 Integer 
... 

tôi nhận được lỗi sau:

Type synonym `Vec2' should have 1 argument, but has been given none 
In the instance declaration for `NList Vec2 Integer' 

Bây giờ, tôi rất mới để nhập cấp lập trình, và tôi hiểu rằng tôi có thể làm điều này theo cách rất lạc hậu. Có thể khởi tạo một từ đồng nghĩa kiểu như thế này không? Có bất kỳ trình thủ thuật loại nào có đề xuất cho các cách tốt hơn để hoàn thành mục tiêu của tôi không?

Trả lời

10

Vấn đề ở đây là Vec2Vec3 được có lẽ tuyên bố như một cái gì đó như thế này:

type Vec2 a = Vec (S (S Z)) a 
type Vec3 a = Vec (S (S (S Z))) a 

Loại từ đồng nghĩa không thể được một phần-áp dụng, vì những lý do phức tạp khác nhau (họ sẽ dẫn đến lambdas loại cấp, mà wreck havoc với tất cả những thứ liên quan đến độ phân giải và suy luận của ví dụ - hãy tưởng tượng nếu bạn có thể xác định type Id a = a và biến nó thành một thể hiện của Monad).

Điều đó có nghĩa là bạn không thể tạo Vec2 một ví dụ về bất kỳ thứ gì, vì bạn không thể sử dụng Vec2 như thể nó là một trình tạo kiểu hoàn toàn có loại * -> *; đó là một "macro" loại có hiệu quả chỉ có thể được áp dụng đầy đủ.

Tuy nhiên, bạn có thể xác định từ đồng nghĩa kiểu như các ứng dụng một phần bản thân:

type Vec2 = Vec (S (S Z)) 
type Vec3 = Vec (S (S (S Z))) 

Đây là tương đương, ngoại trừ trường hợp của bạn sẽ được chấp nhận.

Nếu loại Vec3 của bạn thực sự trông giống như

type Vec3 a = Cons a (Cons a (Cons a Nil))) 

hoặc tương tự, sau đó bạn đang trên may mắn; bạn sẽ phải sử dụng trình bao bọc newtype nếu bạn muốn cung cấp bất kỳ trường hợp nào.(Mặt khác, bạn sẽ có thể để tránh trường hợp xác định trực tiếp bằng các loại hoàn toàn bằng cách cho trường hợp cho NilCons thay vào đó, cho phép bạn sử dụng Vec3 như một ví dụ.)

Lưu ý rằng với GHC 7.4 mới constraint kinds, bạn có thể tránh một loại riêng biệt hoàn toàn, và chỉ cần xác định một từ đồng nghĩa chế:

type NList v i = 
    (Fold (v i) i 
    , Map i i (v i) (v i) 
    , Map i (Maybe i) (v i) (v (Maybe i)) 
    ) 

theo như cách tiếp cận của bạn nói chung đi, nó về cơ bản sẽ làm việc tốt; ý tưởng chung tương tự được sử dụng bởi gói . Số lượng lớn các lớp và bối cảnh lớn có thể làm cho các thông báo lỗi rất khó hiểu và làm chậm quá trình biên dịch, nhưng đó là bản chất của loại tấn công. Tuy nhiên, nếu bạn có một cơ sở Vec kiểu định nghĩa là GADT thông thường:

data Vec n a where 
    Nil :: Vec Z a 
    Succ :: a -> Vec n a -> Vec (S n) a 

sau đó bạn không thực sự cần bất kỳ typeclasses ở tất cả. Nếu nó được định nghĩa trong một số cách khác nhưng vẫn parametrised trên một loại cấp tự nhiên, sau đó bạn có thể thay thế tất cả các lớp học với một:

data Proxy s = Proxy 

class Nat n where 
    natElim 
     :: ((n ~ Z) => r) 
     -> (forall m. (n ~ S m, Nat m) => Proxy m -> r) 
     -> Proxy n 
     -> r 

này sẽ cho phép bạn để loại bỏ các bối cảnh hoàn toàn, nhưng làm cho việc xác định các hoạt động trên các vectơ phức tạp hơn một chút.

+0

Rất tuyệt, cảm ơn. Một điều tôi không nhận được là ví dụ cuối cùng. Có chuyện gì với toán tử Tilda? – So8res

+0

Đó là [ràng buộc bình đẳng] (http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/equality-constraints.html). Nếu bạn có một hạn chế 'Num a' trong phạm vi, bạn có thể sử dụng toán tử số học trên các giá trị của kiểu' a'; nếu bạn có một giới hạn '(a ~ b)' trong phạm vi, bạn có thể sử dụng các giá trị 'a' làm giá trị' b' và ngược lại. Về cơ bản, bạn có thể đọc '(n ~ Z) => r' là" chứng minh với tôi rằng 'n' là' Z', và tôi sẽ cho bạn một 'r'". – ehird

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