Câu hỏi Lấy danh sách các cá thể trong một lớp kiểu trong Haskell


Có cách nào để lập trình có được một danh sách các trường hợp của một lớp loại?

Tôi cho 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 danh sách chúng ngay tại đây (như chuỗi hoặc bất kỳ một số đại diện của chúng).


16
2018-03-22 19:34


gốc


Trên thực tế, 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


Các câu trả lời:


Bạn có thể tạo các cá thể trong phạm vi cho một lớp kiểu đã cho bằng cách sử dụng Khuôn 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 điều 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 khác là hiển thị tất cả các cá thể trong phạm vi cho một lớp kiểu đã cho bằ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ỉ nhận thức được các lớp kiểu trong phạm vi trong bất kỳ mô đun nào (hoặc tại dấu nhắc ghci, v.v.). Vì vậy, nếu bạn gọi showInstances Hàm TH không có import, bạn sẽ chỉ nhận được các instance 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á.


13
2018-03-22 23:00



Nó xuất hiện này chỉ hoạt động cho 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
Tôi đang chạy GHC 7.6.3. Đây là bản cập nhật cho câu trả lời: [ClassInstance] Hiện tại là [InstanceDec]. Ngoài ra, tôi cần chạy :set -XTemplateHaskell trong GHCi để nhân đôi ví dụ. - apolune


Xem tài liệu mẫu haskell: http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html

Sử dụng reify, bạn có thể nhận được một bản ghi thông tin, mà cho một lớp bao gồm danh sách các thể hiện của nó. Bạn cũng có thể dùng isClassInstance và classInstances trực tiếp.


7
2018-03-22 22:54





Điều này sẽ chạy vào rất nhiều vấn đề ngay sau khi bạn nhận được các khai báo cá thể 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 danh sách vô hạn các phiên bản cho Eq - - Bool,[Bool],[[Bool]],[[[Bool]]] và cứ thế, (Bool,Bool), ((Bool,Bool),Bool), (((Bool,Bool),Bool),Bool) etcetera, cùng với nhiều kết hợp khác nhau như ([((Bool,[Bool]),Bool)],Bool) và kể từ đó trở đi. Nó không rõ ràng làm thế nào để đại diện cho chúng trong một String; ngay cả một danh sách TypeRep sẽ yêu cầu một số liệt kê khá thông minh.

Trình biên dịch có thể (cố gắng) suy luận xem một loại là một thể hiện của Eq cho bất kỳ loại đã cho, nhưng nó không đọc trong tất cả các khai báo cá thể trong phạm vi và sau đó chỉ bắt đầu suy luận tất cả các trường hợp có thể, vì đ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ì?


6
2018-03-22 22:48





Tôi đoán, điều đó 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 một kiểu dữ liệu. Ví dụ, chúng ta hãy lấ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 ràng buộc kiểu chữ sau đó được dịch thành một đối số bổ sung có 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 thông qua trong thời gian 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à đủ.


2
2018-03-22 20:27



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 ví dụ. - John L


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 chính nó' ...


0
2018-03-22 21:17