获取Haskell中类型类中的实例列表
有没有一种方法可以通过编程方式获取类型类的实例列表获取Haskell中类型类中的实例列表,haskell,introspection,Haskell,Introspection,有没有一种方法可以通过编程方式获取类型类的实例列表 我觉得编译器必须知道这些信息才能进行类型检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请在这里列出它们(作为字符串或它们的任何表示形式)。我想,这是不可能的。我向您解释了typeclass(用于GHC)的实现,从中可以看出,编译器不需要知道哪些类型是typeclass的实例。它只需要知道特定类型是否为实例 typeclass将被转换为数据类型。作为一个例子,让我们以Eq: class Eq a where (==),
我觉得编译器必须知道这些信息才能进行类型检查和编译代码,所以有没有办法告诉编译器:嘿,你知道那个类的那些实例,请在这里列出它们(作为字符串或它们的任何表示形式)。我想,这是不可能的。我向您解释了typeclass(用于GHC)的实现,从中可以看出,编译器不需要知道哪些类型是typeclass的实例。它只需要知道特定类型是否为实例 typeclass将被转换为数据类型。作为一个例子,让我们以
Eq
:
class Eq a where
(==),(/=) :: a -> a -> Bool
typeclass将被翻译成一种字典,包含其所有功能:
data Eq a = Eq {
(==) :: a -> a -> Bool,
(/=) :: a -> a -> Bool
}
然后将每个typeclass约束转换为包含字典的额外参数:
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
就足够了。对现有类自动执行此操作是不可能的。对于您自己的类及其实例,您可以这样做。您需要通过模板Haskell(或者准引用)声明所有内容,它会自动生成一些奇怪的数据结构,对声明的实例进行编码。定义奇怪的数据结构并让模板Haskell这样做,这些细节留给任何有用例的人
也许您可以在构建中添加一些模板Haskell或其他魔法,将所有源文件作为运行时可用的文本(c.f.程序quine)包含在内。然后您的程序将“grep self”…一旦您得到如下实例声明,就会遇到很多问题
instance Eq a => Eq [a] where
[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
_ == _ = False
及
以及单个具体实例(例如,实例Eq Bool
)
您将获得无限多的实例列表,用于Eq
-Bool
,[[Bool]]
,[[Bool]]]
等等,(Bool,Bool)
,((Bool,Bool),Bool)
,((Bool,Bool),Bool)
等等,以及这些实例的各种组合,如((Bool,Bool.),[Bool]),Bool)
等等。不清楚如何在字符串中表示这些;即使是TypeRep
的列表也需要一些非常聪明的枚举
编译器可以(尝试)推断某个类型是否是任何给定类型的Eq
实例,但它不会读入范围中的所有实例声明,然后只是开始推断所有可能的实例,因为这永远不会结束
当然,重要的问题是,你需要这个做什么 请参阅haskell文档模板:
使用reify
,您可以获得一条信息记录,其中包含类的实例列表。您还可以直接使用isClassInstance
和classInstances
。您可以使用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显示给定类型类范围内的所有实例
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,那么您也会看到所有这些实例。实际上,编译器不知道定义了哪些实例。它唯一能做的就是检查一个类型是否是一个typeclass的实例。编译器只需要知道特定类型是否为实例,但在任何时候,编译器都可以枚举作用域中的所有实例并生成所需的列表。例如,如果您使用:info Num
,GHCi将执行此操作。这似乎仅适用于GHC 7和TH 2.5。在TH2.4.0.1中尝试这样做似乎并没有给我想要的东西。这是新功能吗?我正在运行GHC 7.6.3。下面是答案的更新:[ClassInstance]
现在是[InstanceDec]
。此外,我还需要在GHCi中运行:set-XTemplateHaskell
,以复制该示例。
*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]}]"
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