获取Haskell中类型类中的实例列表

时间:2011-03-22 19:34:06

标签: haskell introspection

有没有办法以编程方式获取类型类的实例列表?

让我觉得编译器必须知道这些信息才能输入检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请把它们放在正确的列表中在这里(作为字符串或其他任何表示)。

5 个答案:

答案 0 :(得分:13)

您可以使用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,那么你也会看到所有这些实例。

答案 1 :(得分:7)

请参阅模板haskell文档:http://hackage.haskell.org/packages/archive/template-haskell/2.5.0.0/doc/html/Language-Haskell-TH.html

使用reify,您可以获得一条信息记录,该记录包含一个类的实例列表。您也可以直接使用isClassInstanceclassInstances

答案 2 :(得分:6)

一旦获得像

这样的实例声明,就会遇到很多问题
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]]]等实例的无限列表,{{1} },(Bool,Bool)((Bool,Bool),Bool)等,以及这些的各种组合,例如(((Bool,Bool),Bool),Bool)等。目前尚不清楚如何在([((Bool,[Bool]),Bool)],Bool)中表示这些内容;即使是String列表也需要一些非常聪明的枚举。

编译器可以(尝试)推断出某个类型是否是任何给定类型的TypeRep的实例,但它不读取范围内的所有实例声明然后刚开始推断所有可能的实例,因为那将永远不会完成!

重要的问题当然是,你需要什么呢?

答案 3 :(得分:2)

我想,这是不可能的。我解释了类型类(对于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就足够了。

答案 4 :(得分:0)

无法为现有类自动执行此操作。对于你自己的班级和实例,你可以做到。您需要通过Template Haskell(或者可能是准引用)声明所有内容,它会自动生成一些编码声明实例的奇怪数据结构。定义奇怪的数据结构并使模板Haskell执行此操作是留给任何具有用例的用户的详细信息。

也许您可以在构建中添加一些模板Haskell或其他魔法,以将所有源文件包含为运行时可用的文本(c.f。program quine)。那么你的程序就会'自我'......