2015-09-18 16 views
8

Hãy giả sử tôi có một lớp kiểu Stack với một ví dụ List:Nhiều thông số loại trong lớp loại?

class Stack a where 
    push :: a -> Integer -> a 
    pop :: a -> a 
    last :: a -> Integer 

data List = Empty | Element Integer List 
instance Stack List where 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Làm thế nào Stack đã được xác định theo thứ tự cho List để không bị giới hạn Integer giá trị?

-- class Stack (?) where ... 
data List a = Empty | Element a (List a) 
-- instance Show (List a) where ... 

Trả lời

6

Trong trường hợp đó bạn có thể làm cho một lớp học đa thông:

class Stack a b where 
    push :: a -> b -> a 
    pop :: a -> a 
    last :: a -> b 

và định nghĩa nó với:

instance Stack (List b) b where --You don't need to use `b`, but this make it easier to understand 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Lưu ý rằng đây không phải là mặc định (tiêu chuẩn) Tính năng Haskell, và bạn sẽ cần bật nó lên. Hoặc bằng cách chuyển -XMultiParamTypeClasses-XFlexibleInstances đến trình biên dịch.

Hoặc bạn có thể viết:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} 

trong tiêu đề của tập tin nguồn của bạn.


Lưu ý rằng có thể có một số b 's cho một a mà bạn xác định một thể hiện (và ngược lại). Điều này có thể làm cho nó khó khăn để làm việc với các lớp như vậy. Nói ví dụ bạn viết một loại Dummy:

data Dummy = Dummy 

bạn có thể định nghĩa:

instance Stack Dummy b where 
    push x = const x 
    pop = id 
    last = const $ error "Dummy object" 

Bây giờ nó có nghĩa là bạn có Stack dụ cho mỗi thể b, do đó bạn có thể pushpop tất cả các loại công cụ để Dummy đối tượng.

+1

Trước đó, tôi thực sự đã thử cú pháp tương tự, nhưng chỉ viết 'instance Stack (List a)'. Và tôi quên gắn thêm 'FlexibleInstances' vào pragma. Bây giờ nó hoạt động, cảm ơn bạn :) – Cubinator73

+0

Nếu mỗi 'a' thừa nhận nhiều nhất một' b' sao cho 'Stack a b' có một cá thể, một phụ thuộc chức năng' a-> b' có thể giúp loại máy inference đáng kể. Nếu không, mỗi cuộc gọi đến 'pop :: a -> a' là mơ hồ:' b' không thể được xác định từ ngữ cảnh của 'pop'. (Các gia đình kiểu cũng có thể được sử dụng, tất nhiên.) – chi

+0

@chi: IIRC bạn có thể thêm các hạn chế, như vậy sẽ chỉ có một 'b' cho' a'. Sẽ tra cứu sau. –

8

Cân nhắc sử dụng biến lớp học cao cấp hơn. Như vậy:

class Stack s where 
    push :: s a -> a -> s a 
    pop :: s a -> s a 
    last :: s a -> a 

data List a = Empty | Element a (List a) 

Thể hiện vẫn còn chính xác như bạn đã viết nó (mặc dù List hiện nay có loại * -> * thay vì *):

instance Stack List where 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Cách tiếp cận này là tinh khiết Haskell 2010 - nó đòi hỏi không có phần mở rộng.

Ngoài ra, hãy xem xét việc thực hiện các lỗi của bạn có thể quan sát được; ví dụ bằng cách thay đổi loại poplast để trả lại Maybe (s a)Maybe a, tương ứng.

+0

Điều này cũng giống như một cách tiếp cận tuyệt vời với tôi. Và tôi chắc chắn đang cố gắng 'Có thể' :) – Cubinator73

+0

@ Cubinator73 Đây là * thường * cách để đi. Phương thức tiếp cận đa thông số với một phụ thuộc chức năng (hoặc một kiểu chữ thông số đơn với một kiểu liên quan) có ý nghĩa nếu bạn đang xem xét nhiều kiểu chồng hình đơn lẻ. – dfeuer

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