2011-03-22 27 views
15

Có cách nào để lập trình danh sách các phiên bản của một loại lớp không? Nó đánh tôi rằng trình biên dịch phải biết thông tin này để kiểm tra và biên dịch mã, vì vậy có cách nào đó để nói với trình biên dịch: hey, bạn biết những trường hợp của lớp đó, hãy đặt một danh sách ngay tại đây (như chuỗi hoặc bất kỳ đại diện nào của chúng).Nhận danh sách các cá thể trong một loại lớp trong Haskell

+0

Thực ra, trình biên dịch không biết trường hợp nào được xác định. Điều duy nhất nó có thể làm, là kiểm tra xem một loại là trường hợp của một typeclass. – fuz

Trả lời

12

Bạn có thể tạo các trường hợp trong phạm vi cho một lớp loại đã cho bằng cách sử dụng Mẫu Haskell.

import Language.Haskell.TH 

-- get a list of instances 
getInstances :: Name -> Q [ClassInstance] 
getInstances typ = do 
    ClassI _ instances <- reify typ 
    return instances 

-- convert the list of instances into an Exp so they can be displayed in GHCi 
showInstances :: Name -> Q Exp 
showInstances typ = do 
    ins <- getInstances typ 
    return . LitE . stringL $ show ins 

Chạy này trong GHCi:

*Main> $(showInstances ''Num) 
"[ClassInstance {ci_dfun = GHC.Num.$fNumInteger, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Integer.Type.Integer]},ClassInstance {ci_dfun = GHC.Num.$fNumInt, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Int]},ClassInstance {ci_dfun = GHC.Float.$fNumFloat, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Float]},ClassInstance {ci_dfun = GHC.Float.$fNumDouble, ci_tvs = [], ci_cxt = [], ci_cls = GHC.Num.Num, ci_tys = [ConT GHC.Types.Double]}]" 

Một kỹ thuật hữu ích đang hiển thị tất cả các trường trong phạm vi cho một lớp học loại nhất định sử dụng GHCi.

Prelude> :info Num 
class (Eq a, Show a) => Num a where 
    (+) :: a -> a -> a 
    (*) :: a -> a -> a 
    (-) :: a -> a -> a 
    negate :: a -> a 
    abs :: a -> a 
    signum :: a -> a 
    fromInteger :: Integer -> a 
    -- Defined in GHC.Num 
instance Num Integer -- Defined in GHC.Num 
instance Num Int -- Defined in GHC.Num 
instance Num Float -- Defined in GHC.Float 
instance Num Double -- Defined in GHC.Float 

Chỉnh sửa: Điều quan trọng cần biết là trình biên dịch chỉ biết các loại lớp trong phạm vi trong bất kỳ mô-đun cụ thể nào (hoặc tại dấu nhắc ghci, v.v.). Vì vậy, nếu bạn gọi hàm showInstances TH không có nhập, bạn sẽ chỉ nhận được các phiên từ Prelude. Nếu bạn có các mô-đun khác trong phạm vi, ví dụ: Data.Word, sau đó bạn sẽ thấy tất cả các trường hợp quá.

+1

Dường như nó chỉ hoạt động với GHC 7 và TH 2.5. Cố gắng làm điều này trong TH 2.4.0.1 dường như không cho tôi những gì tôi muốn. Đây có phải là tính năng mới không? – mentics

+0

Tôi đang chạy GHC 7.6.3. Đây là bản cập nhật cho câu trả lời: '[ClassInstance]' bây giờ là '[InstanceDec]'. Ngoài ra, tôi cần chạy ': set -XTemplateHaskell' trong GHCi để sao chép ví dụ. – apolune

2

Tôi đoán là không thể. Tôi giải thích cho bạn việc thực hiện các typeclasses (cho GHC), từ nó, bạn có thể thấy, trình biên dịch không cần phải biết loại nào là instance của một typeclass. Nó chỉ phải biết, cho dù một loại cụ thể là trường hợp hay không.

Một typeclass sẽ được dịch sang kiểu dữ liệu. Như một ví dụ, chúng ta hãy Eq:

class Eq a where 
    (==),(/=) :: a -> a -> Bool 

Các typeclass sẽ được dịch sang một loại từ điển, có chứa tất cả các chức năng của nó:

data Eq a = Eq { 
    (==) :: a -> a -> Bool, 
    (/=) :: a -> a -> Bool 
    } 

Mỗi chế typeclass sau đó được dịch ra một cuộc tranh cãi thêm chứa từ điển:

elem :: Eq a => a -> [a] -> Bool 
elem _ [] = False 
elem a (x:xs) | x == a = True 
       | otherwise = elem a xs 

trở thành:

elem :: Eq a -> a -> [a] -> Bool 
elem _ _ [] = False 
elem eq a (x:xs) | (==) eq x a = True 
       | otherwise = elem eq a xs 

Điều quan trọng là từ điển sẽ được chuyển qua khi chạy. Hãy tưởng tượng, dự án của bạn chứa nhiều mô-đun. GHC không phải kiểm tra tất cả các mô-đun cho các trường hợp, nó chỉ cần tra cứu, cho dù một cá thể được định nghĩa ở bất kỳ đâu.

Nhưng nếu bạn có sẵn nguồn, tôi đoán kiểu cũ grep cho các trường hợp sẽ là đủ.

+1

Không chính xác. Trình biên dịch chỉ cần biết nếu một kiểu cụ thể là một cá thể, tuy nhiên bất cứ lúc nào trình biên dịch có thể liệt kê tất cả các cá thể trong phạm vi và tạo ra danh sách mong muốn. GHCi sẽ làm điều này nếu bạn sử dụng ': info Num' chẳng hạn. –

0

Không thể tự động thực hiện việc này cho các lớp hiện có. Đối với lớp học của riêng bạn và các trường hợp của bạn, bạn có thể làm điều đó. Bạn sẽ cần khai báo mọi thứ thông qua Template Haskell (hoặc có lẽ là bán dẫn) và nó sẽ tự động tạo ra một số cấu trúc dữ liệu lạ mã hóa các cá thể đã khai báo. Xác định cấu trúc dữ liệu lạ và làm cho mẫu Haskell làm điều này là các chi tiết còn lại cho bất kỳ ai có trường hợp sử dụng cho chúng.

Có lẽ bạn có thể thêm một số mẫu Haskell hoặc ma thuật khác vào bản dựng của bạn để bao gồm tất cả các tệp nguồn dưới dạng văn bản có sẵn tại thời gian chạy (chuỗi chương trình c.f.). Sau đó, chương trình của bạn sẽ 'grep bản thân' ...

6

này sẽ chạy vào rất nhiều vấn đề càng sớm càng bạn nhận được tờ khai dụ như

instance Eq a => Eq [a] where 
    [] == [] = True 
    (x:xs) == (y:ys) = x == y && xs == ys 
    _ == _ = False 

instance (Eq a,Eq b) => Eq (a,b) where 
    (a1,b1) == (a2,b2) = a1 == a2 && b1 == b2 

cùng với một ví dụ cụ thể (ví dụ: instance Eq Bool).

Bạn sẽ nhận được một danh sách vô hạn các trường hợp cho Eq-Bool, [Bool], [[Bool]], [[[Bool]]] và như vậy, (Bool,Bool), ((Bool,Bool),Bool), (((Bool,Bool),Bool),Bool) etcetera, cùng với sự kết hợp khác nhau của các như ([((Bool,[Bool]),Bool)],Bool) và vân vân.Nó không rõ ràng làm thế nào để đại diện cho những trong một String; ngay cả một danh sách của TypeRep sẽ yêu cầu một số liệt kê khá thông minh.

Trình biên dịch thể (cố gắng) suy ra liệu một loại là một thể hiện của Eq cho bất kỳ loại nào đó, nhưng nó không đọc trong tất cả các tờ khai dụ trong phạm vi và sau đó chỉ cần bắt đầu suy luận tất cả các trường có thể, kể từ điều đó sẽ không bao giờ kết thúc!

Câu hỏi quan trọng là tất nhiên, bạn cần điều này để làm gì?

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