Получить список экземпляров в классе типов в Haskell - PullRequest
17 голосов
/ 22 марта 2011

Есть ли способ программно получить список экземпляров класса типа?

Мне кажется, что компилятор должен знать эту информацию, чтобы проверять тип и компилировать код, поэтому есть какой-то способ сообщить компилятору: эй, вы знаете эти экземпляры этого класса, пожалуйста, составьте их список правильно здесь (как строки или как-то еще их представление).

Ответы [ 5 ]

13 голосов
/ 23 марта 2011

Вы можете сгенерировать экземпляры в области видимости для данного класса типов, используя Template 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

Запуск этого в 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]}]"

Еще один полезный метод - показать все экземпляры вобласть действия для данного класса типов с использованием 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

Редактирование: важно знать, что компилятор знает только о классах типов в области действия в любом данном модуле (или в приглашении ghci и т. д.).Поэтому, если вы вызовете функцию showInstances TH без импорта, вы получите только экземпляры из Prelude.Если у вас есть другие модули в области, например, Data.Word, то вы также увидите все эти экземпляры.

7 голосов
/ 23 марта 2011

См. Документацию шаблона haskell: http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html

Используя reify, вы можете получить инфо-запись, которая для класса включает в себя список экземпляров.Вы также можете использовать isClassInstance и classInstances напрямую.

6 голосов
/ 23 марта 2011

Это столкнется с множеством проблем, как только вы получите объявления экземпляров, такие как

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

вместе с одним конкретным экземпляром (например, instance Eq Bool).

Вы получите бесконечный список экземпляров для Eq - Bool, [Bool], [[Bool]], [[[Bool]]] и т. Д., (Bool,Bool), ((Bool,Bool),Bool), (((Bool,Bool),Bool),Bool) и так далее, а также с различными их комбинациями, такими как ([((Bool,[Bool]),Bool)],Bool) и так далее. Не ясно, как представить их в String; даже список TypeRep потребовал бы довольно умного перечисления.

Компилятор может (попытаться) определить, является ли тип экземпляром Eq для какого-либо данного типа, но он не читает все объявления экземпляров в области видимости, а затем просто начинает выводить все возможные случаи, так как это никогда не закончится!

Важный вопрос, конечно, зачем вам это нужно?

2 голосов
/ 22 марта 2011

Я думаю, это невозможно.Я объясняю вам реализацию классов типов (для GHC), из этого вы можете видеть, что компилятору не нужно знать, какие типы являются экземплярами класса типов.Нужно только знать, является ли конкретный тип экземпляром или нет.

Класс типов будет переведен в тип данных.В качестве примера возьмем Eq:

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

Класс типов будет переведен в своего рода словарь, содержащий все его функции:

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

Затем будет переведено каждое ограничение класса типов.в дополнительный аргумент, содержащий словарь:

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

становится:

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

Важно то, что словарь будет передан во время выполнения .Представьте, ваш проект содержит много модулей.GHC не должен проверять все модули на наличие экземпляров, он просто должен посмотреть, определен ли экземпляр где-либо.

Но если у вас есть доступный источник, я думаю, что в старом стиле grepдля экземпляров будет достаточно.

0 голосов
/ 23 марта 2011

Невозможно автоматически сделать это для существующих классов. Для вашего собственного класса и его экземпляров вы можете сделать это. Вам нужно будет объявить все через Template Haskell (или, возможно, квази-цитирование), и он автоматически сгенерирует некоторую странную структуру данных, которая кодирует объявленные экземпляры. Определение странной структуры данных и заставление Template Haskell сделать это - детали, оставленные для тех, у кого есть для них сценарий использования.

Возможно, вы могли бы добавить шаблон Haskell или другую магию в вашу сборку, чтобы включить все исходные файлы в виде текста, доступного во время выполнения (например, программа quine). Тогда ваша программа "сама себя" ...

...